├── .gitignore
├── Readme.md
└── apidecomp
├── ApiWriter.cs
├── Program.cs
└── apidecomp.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | obj
3 | .vscode
4 | archive
5 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Unity API Changes
2 |
3 | This repository contains the decompiled public API of Unity 3D to allow comparing the API between Unity versions.
4 |
5 | **This project is experimental.** The API decompilation is still work in progress and there might be incorrect or missing information. For now, the project only contains major releases and the main UnityEngine and UnityEditor assemblies. Issue reports and feature requests welcome.
6 |
7 | Use the table below to compare the API of two Unity versions.
8 |
9 | ## Versions
10 |
11 | From→To | 2022.1 | 2021.2 | 2021.1 | 2020.3 | 2019.4
12 | --- | --- | --- | --- | --- | ---
13 | 2021.2 | [2021.2→2022.1] | - | - | - | -
14 | 2021.1 | [2021.1→2022.1] | [2021.1→2021.2] | - | - | -
15 | 2020.3 | [2020.3→2022.1] | [2020.3→2021.2] | [2020.3→2021.1] | - | -
16 | 2019.4 | [2019.4→2022.1] | [2019.4→2021.2] | [2019.4→2021.1] | [2019.4→2020.3] | -
17 | 2018.4 | [2018.4→2022.1] | [2018.4→2021.2] | [2018.4→2021.1] | [2018.4→2020.3] | [2018.4→2019.4]
18 |
19 | [2021.2→2022.1]: https://github.com/sttz/unity-api-diff/compare/unity/2021.2..unity/2022.1
20 |
21 | [2021.1→2022.1]: https://github.com/sttz/unity-api-diff/compare/unity/2021.1..unity/2022.1
22 | [2021.1→2021.2]: https://github.com/sttz/unity-api-diff/compare/unity/2021.1..unity/2021.2
23 |
24 | [2020.3→2022.1]: https://github.com/sttz/unity-api-diff/compare/unity/2020.3..unity/2022.1
25 | [2020.3→2021.2]: https://github.com/sttz/unity-api-diff/compare/unity/2020.3..unity/2021.2
26 | [2020.3→2021.1]: https://github.com/sttz/unity-api-diff/compare/unity/2020.3..unity/2021.1
27 |
28 | [2019.4→2022.1]: https://github.com/sttz/unity-api-diff/compare/unity/2019.4..unity/2022.1
29 | [2019.4→2021.2]: https://github.com/sttz/unity-api-diff/compare/unity/2019.4..unity/2021.2
30 | [2019.4→2021.1]: https://github.com/sttz/unity-api-diff/compare/unity/2019.4..unity/2021.1
31 | [2019.4→2020.3]: https://github.com/sttz/unity-api-diff/compare/unity/2019.4..unity/2020.3
32 |
33 | [2018.4→2022.1]: https://github.com/sttz/unity-api-diff/compare/unity/2018.4..unity/2022.1
34 | [2018.4→2021.2]: https://github.com/sttz/unity-api-diff/compare/unity/2018.4..unity/2021.2
35 | [2018.4→2021.1]: https://github.com/sttz/unity-api-diff/compare/unity/2018.4..unity/2021.1
36 | [2018.4→2020.3]: https://github.com/sttz/unity-api-diff/compare/unity/2018.4..unity/2020.3
37 | [2018.4→2019.4]: https://github.com/sttz/unity-api-diff/compare/unity/2018.4..unity/2019.4
38 |
--------------------------------------------------------------------------------
/apidecomp/ApiWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.IO;
5 | using System.Linq;
6 | using Mono.Cecil;
7 |
8 | namespace apidiff
9 | {
10 |
11 | ///
12 | /// Extract the public API of a C# assembly, to make changes
13 | /// to the API easily diffable.
14 | ///
15 | public static class ApiWriter
16 | {
17 | public static void WriteApi(string assemblyPath, string outputPath)
18 | {
19 | var resolver = new DefaultAssemblyResolver();
20 | resolver.AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
21 |
22 | var config = new ReaderParameters();
23 | config.AssemblyResolver = resolver;
24 |
25 | var module = ModuleDefinition.ReadModule(assemblyPath, config);
26 | Console.WriteLine($"Opened module {module.Name}");
27 |
28 | var types = module.Types
29 | .Where(t => t.IsPublic)
30 | .OrderBy(t => t.Name)
31 | .ToList();
32 |
33 | var count = types.Count;
34 | Console.WriteLine($"Found {types.Count} public root types");
35 | if (count == 0) return;
36 |
37 | var moduleOutputPath = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(module.Name));
38 | if (!Directory.Exists(moduleOutputPath)) {
39 | Directory.CreateDirectory(moduleOutputPath);
40 | }
41 |
42 | foreach (var type in types) {
43 | WriteType(type, 0, moduleOutputPath);
44 | }
45 | }
46 |
47 | // -------- Main Types --------
48 |
49 | static void WriteType(TypeDefinition type, int indent, string basePath)
50 | {
51 | var namespacePath = Path.Combine(basePath, type.Namespace.Replace(".", "/"));
52 | if (!Directory.Exists(namespacePath)) {
53 | Directory.CreateDirectory(namespacePath);
54 | }
55 |
56 | var filename = RemoveGenericSuffix(type.Name);
57 | if (type.HasGenericParameters) {
58 | filename += "<" + string.Join(", ", type.GenericParameters.Select(p => FormatTypeName(p))) + ">";
59 | }
60 | filename += ".cs";
61 |
62 | var filePath = Path.Combine(namespacePath, filename);
63 | if (File.Exists(filePath)) {
64 | Console.WriteLine($"WARNING File already exists: {filePath}");
65 | }
66 |
67 | using (var stream = File.Open(filePath, FileMode.Create, FileAccess.Write)) {
68 | using (var output = new StreamWriter(stream)) {
69 | foreach (var imported in ImportedNamespaces) {
70 | WriteIndent(indent, output);
71 | output.WriteLine($"using {imported};");
72 | }
73 |
74 | output.WriteLine();
75 |
76 | WriteIndent(indent, output);
77 | output.Write("namespace ");
78 | output.WriteLine(type.Namespace);
79 |
80 | WriteIndent(indent, output);
81 | output.WriteLine("{");
82 |
83 | output.WriteLine();
84 |
85 | WriteType(type, indent, output);
86 |
87 | output.WriteLine();
88 |
89 | WriteIndent(indent, output);
90 | output.WriteLine("}");
91 | }
92 | }
93 | }
94 |
95 | static void WriteType(TypeDefinition type, int indent, TextWriter output)
96 | {
97 | if (type.IsEnum) {
98 | WriteEnum(type, indent, output);
99 | } else if (type.BaseType != null && type.BaseType.FullName == "System.MulticastDelegate") {
100 | WriteDelegate(type, indent, output);
101 | } else if (type.IsClass || type.IsValueType || type.IsInterface) {
102 | WriteClassStructOrInterface(type, indent, output);
103 | }
104 | }
105 |
106 | static void WriteClassStructOrInterface(TypeDefinition type, int indent, TextWriter output)
107 | {
108 | WriteIndent(indent, output);
109 |
110 | output.Write(FormatTypeAccess(type));
111 |
112 | if (type.IsSealed && type.IsAbstract) output.Write(" static");
113 | else if (type.IsSealed && !type.IsValueType) output.Write(" sealed");
114 | else if (type.IsAbstract && !type.IsInterface) output.Write(" abstract");
115 |
116 | if (type.IsValueType) output.Write(" struct");
117 | else if (type.IsClass) output.Write(" class");
118 | else if (type.IsInterface) output.Write(" interface");
119 | else throw new Exception("Type not a class, struct or interface.");
120 |
121 | output.Write(" " + FormatTypeName(type, forceNoNamespace: true));
122 |
123 | var hasBaseType = type.BaseType != null && type.BaseType.FullName != "System.Object" && type.BaseType.FullName != "System.ValueType";
124 | if (hasBaseType || type.HasInterfaces) {
125 | var extends = type.Interfaces.Select(i => i.InterfaceType);
126 | if (hasBaseType) extends = extends.Prepend(type.BaseType);
127 | output.Write(" : " + string.Join(", ", extends.Select(t => FormatTypeName(t))));
128 | }
129 |
130 | output.WriteLine();
131 |
132 | WriteIndent(indent, output);
133 | output.WriteLine("{");
134 |
135 | var fields = type.Fields
136 | .Where(f => (f.IsPublic || f.IsFamily) && !f.IsSpecialName)
137 | .OrderBy(f => f.Name);
138 | var props = type.Properties
139 | .Where(p => { var a = GetMoreAccessibleAccessor(p); return a.IsPublic || a.IsFamily; })
140 | .OrderBy(p => p.Name);
141 | var events = type.Events
142 | .Where(e => e.AddMethod.IsPublic || e.AddMethod.IsFamily)
143 | .OrderBy(e => e.Name);
144 | var methods = type.Methods
145 | .Where(m => (m.IsPublic || m.IsFamily) && (!m.IsSpecialName || m.IsConstructor) && m.Name != "Finalize")
146 | .OrderBy(m => m, MethodComparer.Instance);
147 |
148 | WriteMembers(
149 | fields.Where(m => m.IsStatic),
150 | WriteField, indent, output
151 | );
152 | WriteMembers(
153 | props.Where(m => GetMoreAccessibleAccessor(m).IsStatic),
154 | WriteProperty, indent, output
155 | );
156 | WriteMembers(
157 | events.Where(m => m.AddMethod.IsStatic),
158 | WriteEvent, indent, output
159 | );
160 | WriteMembers(
161 | methods.Where(m => m.IsStatic),
162 | WriteMethod, indent, output
163 | );
164 |
165 | WriteMembers(
166 | fields.Where(m => !m.IsStatic),
167 | WriteField, indent, output
168 | );
169 | WriteMembers(
170 | props.Where(m => !GetMoreAccessibleAccessor(m).IsStatic),
171 | WriteProperty, indent, output
172 | );
173 | WriteMembers(
174 | events.Where(m => !m.AddMethod.IsStatic),
175 | WriteEvent, indent, output
176 | );
177 |
178 | WriteMembers(
179 | methods.Where(m => !m.IsStatic && m.IsConstructor),
180 | WriteMethod, indent, output
181 | );
182 | WriteMembers(
183 | methods.Where(m => !m.IsStatic && !m.IsConstructor),
184 | WriteMethod, indent, output
185 | );
186 |
187 | foreach (var nested in type.NestedTypes.OrderBy(t => t.Name)) {
188 | if (!nested.IsNestedPublic && !nested.IsNestedFamily) continue;
189 | WriteType(nested, indent + 1, output);
190 | output.WriteLine();
191 | }
192 |
193 | WriteIndent(indent, output);
194 | output.WriteLine("}");
195 | }
196 |
197 | static void WriteMembers(IEnumerable members, Action writer, int indent, TextWriter output)
198 | where T : IMemberDefinition
199 | {
200 | if (members.Any()) {
201 | foreach (var member in members) {
202 | WriteIndent(indent + 1, output);
203 | writer(member, output);
204 | }
205 | output.WriteLine();
206 | }
207 | }
208 |
209 | static void WriteEnum(TypeDefinition type, int indent, TextWriter output)
210 | {
211 | WriteIndent(indent, output);
212 |
213 | output.Write(FormatTypeAccess(type));
214 | output.Write(" enum");
215 | output.Write(" " + type.Name);
216 |
217 | var valueField = type.Fields.FirstOrDefault(f => !f.IsStatic);
218 | if (valueField != null && valueField.FieldType.FullName != "System.Int32") {
219 | output.Write(" : " + FormatTypeName(valueField.FieldType));
220 | }
221 |
222 | output.WriteLine();
223 |
224 | WriteIndent(indent, output);
225 | output.WriteLine("{");
226 |
227 | foreach (var field in type.Fields.OrderBy(f => f.Constant)) {
228 | if (!field.IsStatic) continue;
229 | WriteIndent(indent + 1, output);
230 | output.WriteLine(field.Name + " = " + FormatConstant(field.Constant) + ",");
231 | }
232 |
233 | WriteIndent(indent, output);
234 | output.WriteLine("}");
235 | }
236 |
237 | static void WriteDelegate(TypeDefinition type, int indent, TextWriter output)
238 | {
239 | WriteIndent(indent, output);
240 |
241 | output.Write(FormatTypeAccess(type));
242 | output.Write(" delegate");
243 |
244 | var invoke = type.Methods.FirstOrDefault(m => m.Name == "Invoke");
245 | output.Write(" " + FormatTypeName(invoke.ReturnType));
246 |
247 | output.Write(" " + type.Name);
248 |
249 | output.Write("(" + string.Join(", ", invoke.Parameters.Select(FormatParameter)) + ")");
250 |
251 | output.WriteLine(";");
252 | }
253 |
254 | // -------- Members --------
255 |
256 | static void WriteField(FieldDefinition field, TextWriter output)
257 | {
258 | if (field.IsStatic)
259 | output.Write("static ");
260 |
261 | if (field.IsPublic) {
262 | output.Write("public ");
263 | } else if (field.IsAssembly) {
264 | output.Write("internal ");
265 | } else if (field.IsFamily) {
266 | output.Write("protected ");
267 | } else if (field.IsFamilyOrAssembly) {
268 | output.Write("protected internal ");
269 | }
270 |
271 | output.Write(FormatTypeName(field.FieldType) + " ");
272 | output.Write(field.Name);
273 |
274 | if (field.HasConstant) {
275 | output.Write(" = " + FormatConstant(field.Constant));
276 | }
277 |
278 | output.WriteLine(";");
279 | }
280 |
281 | static void WriteProperty(PropertyDefinition property, TextWriter output)
282 | {
283 | var eitherMethod = property.GetMethod ?? property.SetMethod;
284 | var otherMethod = eitherMethod == property.SetMethod ? null : property.SetMethod;
285 |
286 | if (eitherMethod.IsStatic)
287 | output.Write("static ");
288 |
289 | string mainAccess;
290 | if (otherMethod == null) {
291 | mainAccess = FormatMethodAccess(eitherMethod);
292 | } else {
293 | mainAccess = FormatMethodAccess(GetMoreAccessibleAccessor(property));
294 | }
295 | output.Write(mainAccess + " ");
296 |
297 | output.Write(FormatTypeName(property.PropertyType) + " ");
298 |
299 | if (property.Name == "Item" && property.HasParameters) {
300 | output.Write("this[" + string.Join(", ", property.Parameters.Select(FormatParameter)) + "]");
301 | } else {
302 | output.Write(property.Name);
303 | }
304 |
305 | output.Write(" { ");
306 |
307 | if (property.GetMethod != null) {
308 | var access = FormatMethodAccess(property.GetMethod);
309 | if (access != mainAccess) output.Write(access + " ");
310 | output.Write("get; ");
311 | }
312 | if (property.SetMethod != null) {
313 | var access = FormatMethodAccess(property.SetMethod);
314 | if (access != mainAccess) output.Write(access + " ");
315 | output.Write("set; ");
316 | }
317 |
318 | output.Write("}");
319 |
320 | if (property.HasConstant) {
321 | output.Write(" = " + FormatConstant(property.Constant));
322 | }
323 |
324 | output.WriteLine();
325 | }
326 |
327 | static void WriteEvent(EventDefinition ev, TextWriter output)
328 | {
329 | var method = ev.AddMethod;
330 |
331 | if (method.IsStatic)
332 | output.Write("static ");
333 |
334 | if (method.IsPublic) {
335 | output.Write("public ");
336 | } else if (method.IsAssembly) {
337 | output.Write("internal ");
338 | } else if (method.IsFamily) {
339 | output.Write("protected ");
340 | } else if (method.IsFamilyOrAssembly) {
341 | output.Write("protected internal ");
342 | } else if (method.IsFamilyAndAssembly) {
343 | output.Write("private protected ");
344 | } else {
345 | output.Write("private ");
346 | }
347 |
348 | output.Write("event ");
349 |
350 | output.Write(FormatTypeName(ev.EventType) + " ");
351 |
352 | output.WriteLine(ev.Name + ";");
353 | }
354 |
355 | static void WriteMethod(MethodDefinition method, TextWriter output)
356 | {
357 | if (method.IsStatic)
358 | output.Write("static ");
359 |
360 | output.Write(FormatMethodAccess(method) + " ");
361 |
362 | if (method.Name == ".ctor") {
363 | output.Write(RemoveGenericSuffix(method.DeclaringType.Name));
364 | } else {
365 | output.Write(FormatTypeName(method.ReturnType) + " ");
366 | output.Write(method.Name);
367 | if (method.HasGenericParameters) {
368 | output.Write("<" + string.Join(", ", method.GenericParameters.Select(p => FormatTypeName(p))) + ">");
369 | }
370 | }
371 |
372 | output.WriteLine("(" + string.Join(", ", method.Parameters.Select(FormatParameter)) + ");");
373 | }
374 |
375 | public class MethodComparer : IComparer
376 | {
377 | public static readonly MethodComparer Instance = new MethodComparer();
378 |
379 | public int Compare(MethodDefinition x, MethodDefinition y)
380 | {
381 | if (x == y) return 0;
382 |
383 | // First sort by method name
384 | var value = string.Compare(x.Name, y.Name, true);
385 | if (value != 0) return value;
386 |
387 | // Then sort by generic parameter count
388 | value = x.GenericParameters.Count.CompareTo(y.GenericParameters.Count);
389 | if (value != 0) return value;
390 |
391 | // Then by generic parameter names
392 | for (int i = 0; i < x.GenericParameters.Count; i++) {
393 | value = string.Compare(x.GenericParameters[i].Name, y.GenericParameters[i].Name, true);
394 | if (value != 0) return value;
395 | }
396 |
397 | // Then sort by parameter count
398 | value = x.Parameters.Count.CompareTo(y.Parameters.Count);
399 | if (value != 0) return value;
400 |
401 | // Then by parameter names
402 | for (int i = 0; i < x.Parameters.Count; i++) {
403 | value = string.Compare(x.Parameters[i].Name, y.Parameters[i].Name, true);
404 | if (value != 0) return value;
405 | }
406 |
407 | // Finally by parameter types
408 | for (int i = 0; i < x.Parameters.Count; i++) {
409 | value = string.Compare(x.Parameters[i].ParameterType.FullName, y.Parameters[i].ParameterType.FullName, true);
410 | if (value != 0) return value;
411 | }
412 |
413 | //var writer = new StringWriter();
414 | //writer.Write("- ");
415 | //WriteMethod(x, writer);
416 | //writer.Write("- ");
417 | //WriteMethod(y, writer);
418 | //Console.WriteLine($"WARNING Method names and parameter types/names match:\n{writer}");
419 |
420 | // This can happen if there are methods that just differ in case,
421 | // e.g. when the case has been corrected but the old method remains
422 | // for backwards compatibility
423 | return 0;
424 | }
425 | }
426 |
427 | // -------- Helpers --------
428 |
429 | const string Indent = " ";
430 |
431 | static readonly string[] ImportedNamespaces = new string[] {
432 | "System",
433 | "System.Collections",
434 | "System.Collections.Generic",
435 | "UnityEngine",
436 | };
437 |
438 | static void WriteIndent(int indent, TextWriter output)
439 | {
440 | for (int i = 0; i < indent; i++) {
441 | output.Write(Indent);
442 | }
443 | }
444 |
445 | static string FormatTypeAccess(TypeDefinition type)
446 | {
447 | if (type.IsPublic || type.IsNestedPublic) {
448 | return "public";
449 | } else if (type.IsNestedAssembly) {
450 | return "internal";
451 | } else if (type.IsNestedFamily) {
452 | return "protected";
453 | } else if (type.IsNestedFamilyOrAssembly) {
454 | return "protected internal";
455 | } else if (type.IsNestedFamilyAndAssembly) {
456 | return "private protected";
457 | } else {
458 | return "private";
459 | }
460 | }
461 |
462 | static string RemoveGenericSuffix(string name)
463 | {
464 | var apo = name.LastIndexOf('`');
465 | if (apo >= 0) {
466 | return name.Substring(0, apo);
467 | } else {
468 | return name;
469 | }
470 | }
471 |
472 | static string FormatTypeName(TypeReference type, bool forceNoNamespace = false)
473 | {
474 | var resolved = type.Resolve() ?? type;
475 |
476 | string name;
477 | if (type.IsArray) {
478 | name = type.Name;
479 | } else {
480 | name = resolved.Name;
481 | }
482 |
483 | if (resolved.Namespace == "System") {
484 | switch (resolved.Name) {
485 | case "Void":
486 | name = name.Replace("Void", "void");
487 | break;
488 | case "Object":
489 | name = name.Replace("Object", "object");
490 | break;
491 | case "Boolean":
492 | name = name.Replace("Boolean", "bool");
493 | break;
494 | case "Int16":
495 | name = name.Replace("Int16", "short");
496 | break;
497 | case "UInt16":
498 | name = name.Replace("UInt16", "ushort");
499 | break;
500 | case "Int32":
501 | name = name.Replace("Int32", "int");
502 | break;
503 | case "UInt32":
504 | name = name.Replace("UInt32", "uint");
505 | break;
506 | case "Int64":
507 | name = name.Replace("Int64", "long");
508 | break;
509 | case "UInt64":
510 | name = name.Replace("UInt64", "ulong");
511 | break;
512 | case "Single":
513 | name = name.Replace("Single", "float");
514 | break;
515 | case "Double":
516 | name = name.Replace("Double", "double");
517 | break;
518 | case "Decimal":
519 | name = name.Replace("Decimal", "decimal");
520 | break;
521 | case "Byte":
522 | name = name.Replace("Byte", "byte");
523 | break;
524 | case "SByte":
525 | name = name.Replace("SByte", "sbyte");
526 | break;
527 | case "String":
528 | name = name.Replace("String", "string");
529 | break;
530 | case "Char":
531 | name = name.Replace("Char", "char");
532 | break;
533 | }
534 | }
535 |
536 | if (name.EndsWith("&")) {
537 | name = name.Substring(0, name.Length - 1);
538 | }
539 |
540 | if (!forceNoNamespace
541 | && resolved.Namespace != ""
542 | && !ImportedNamespaces.Contains(resolved.Namespace)) {
543 | if (resolved.Namespace.StartsWith("UnityEngine.")) {
544 | name = resolved.Namespace.Substring(12) + "." + name;
545 | } else {
546 | name = resolved.Namespace + "." + name;
547 | }
548 | }
549 |
550 | if (resolved.GenericParameters.Count > 0) {
551 | name = RemoveGenericSuffix(name);
552 | if (type is GenericInstanceType instanceType) {
553 | name += "<" + string.Join(", ", instanceType.GenericArguments.Select(p => FormatTypeName(p))) + ">";
554 | } else {
555 | name += "<" + string.Join(", ", resolved.GenericParameters.Select(p => FormatTypeName(p))) + ">";
556 | }
557 | }
558 |
559 | return name;
560 | }
561 |
562 | static string FormatConstant(object constant)
563 | {
564 | if (constant == null) {
565 | return "null";
566 | } else if (constant is string) {
567 | return $"\"{constant}\"";
568 | } else if (constant is bool) {
569 | return ((bool)constant ? "true" : "false");
570 | } else {
571 | return constant.ToString();
572 | }
573 | }
574 |
575 | static MethodDefinition GetMoreAccessibleAccessor(PropertyDefinition property)
576 | {
577 | if (property.GetMethod == null) {
578 | return property.SetMethod;
579 | } else if (property.SetMethod == null) {
580 | return property.GetMethod;
581 | }
582 |
583 | var getAccess = GetAccessRank(property.GetMethod);
584 | var setAccess = GetAccessRank(property.SetMethod);
585 | return getAccess < setAccess ? property.GetMethod : property.SetMethod;
586 | }
587 |
588 | static int GetAccessRank(MethodDefinition method)
589 | {
590 | if (method.IsPublic) {
591 | return 1;
592 | } else if (method.IsAssembly) {
593 | return 2;
594 | } else if (method.IsFamily) {
595 | return 2;
596 | } else if (method.IsFamilyOrAssembly) {
597 | return 2;
598 | } else if (method.IsFamilyAndAssembly) {
599 | return 3;
600 | } else {
601 | return 3;
602 | }
603 | }
604 |
605 | static string FormatMethodAccess(MethodDefinition method)
606 | {
607 | if (method.IsPublic) {
608 | return "public";
609 | } else if (method.IsAssembly) {
610 | return "internal";
611 | } else if (method.IsFamily) {
612 | return "protected";
613 | } else if (method.IsFamilyOrAssembly) {
614 | return "protected internal";
615 | } else if (method.IsFamilyAndAssembly) {
616 | return "private protected";
617 | } else {
618 | return "private";
619 | }
620 | }
621 |
622 | static string FormatParameter(ParameterDefinition param)
623 | {
624 | var name = "";
625 |
626 | if (param.IsIn && param.IsOut) name += "ref ";
627 | else if (param.IsIn) name += "in ";
628 | else if (param.IsOut) name += "out ";
629 |
630 | name += FormatTypeName(param.ParameterType) + " " + param.Name;
631 |
632 | if (param.HasConstant) {
633 | name += " = " + FormatConstant(param.Constant);
634 | }
635 |
636 | return name;
637 | }
638 | }
639 |
640 | }
641 |
--------------------------------------------------------------------------------
/apidecomp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace apidiff
5 | {
6 |
7 | class Program
8 | {
9 | // -------- CLI --------
10 |
11 | static void Main(string[] args)
12 | {
13 | if (args.Length != 2) {
14 | PrintHelp();
15 | Environment.Exit(1);
16 | }
17 |
18 | var path = args[0];
19 | var outputPath = args[1];
20 |
21 | if (!Directory.Exists(path)) {
22 | Console.WriteLine($"Path is not a directory: {path}");
23 | PrintHelp();
24 | Environment.Exit(2);
25 | }
26 |
27 | var editorPaths = Directory.GetFiles(path, "UnityEditor*.dll");
28 | if (editorPaths.Length == 0) {
29 | Console.WriteLine($"Could not find UnityEditor assemblies at path: {path}");
30 | PrintHelp();
31 | Environment.Exit(3);
32 | }
33 |
34 | var enginePaths = Directory.GetFiles(path, "UnityEngine*.dll");
35 | if (enginePaths.Length == 0) {
36 | Console.WriteLine($"Could not find UnityEngine assemblies at path: {path}");
37 | PrintHelp();
38 | Environment.Exit(3);
39 | }
40 |
41 | if (Directory.Exists(outputPath)) {
42 | var contents = Directory.GetDirectories(outputPath);
43 | foreach (var dir in contents) {
44 | if (Path.GetFileName(dir).StartsWith(".")) continue;
45 | Directory.Delete(dir, true);
46 | }
47 | }
48 |
49 | Directory.CreateDirectory(outputPath);
50 |
51 | foreach (var assembly in editorPaths) {
52 | Console.WriteLine($"");
53 | ApiWriter.WriteApi(assembly, outputPath);
54 | }
55 |
56 | foreach (var assembly in enginePaths) {
57 | Console.WriteLine($"");
58 | ApiWriter.WriteApi(assembly, outputPath);
59 | }
60 | }
61 |
62 | static void PrintHelp()
63 | {
64 | Console.WriteLine("Public API Printer");
65 | Console.WriteLine("usage: apidecomp PATH_TO_ASSEMBLIES OUTPUT_PATH");
66 | }
67 |
68 |
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/apidecomp/apidecomp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 | $(DefaultItemExcludes);Output\**\*
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------