Search Unity

  1. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  2. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  3. Participate with students all over the world and build projects to teach people. Join now!
    Dismiss Notice
  4. Build games and experiences that can load instantly and without install. Explore the Project Tiny Preview today!
    Dismiss Notice
  5. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  6. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

YAML fileID hash function for DLL scripts

Discussion in 'Editor & General Support' started by genail, Jun 16, 2014.

  1. genail

    genail

    Joined:
    Jul 2, 2012
    Posts:
    1,023
    Do anybody know what hash function (if any) is used to compute fileID number for DLL script references? What I am talking about is this:

    m_Script: {fileID: -1167294237, guid: 2b16a1acf52a2a64e916f8a9e6d5df31, type: 3}

    I've found out some interesting things posts this one, but I cannot find actual hash function to compute fileID.

    Why I need this? I am Asset Store publisher and I wanted to create a evaluation version of my tools. My tools consists of scripts, prefabs, materials and scenes, so when I will compile scripts into DLLs the rest will break. If I had method to compute the fileID for compiled scripts I would create a ruby script to fix all references and share it with the community.
     
  2. Lambda Knight

    Lambda Knight

    Joined:
    Mar 8, 2011
    Posts:
    23
    I know!

    Given a type t, the fileID is equal to the first four bytes of the MD4 of the string `"s\0\0\0" + t.Namespace + t.Name' as a little endian 32-byte integer.

    Below is a chunk of code that will compute the fileID. The MD4 is the first, simple MD4 C# implementation, so feel free to drop in your own if you want. The important part is the FileIDUtil.Compute function.

    Code (CSharp):
    1. using System;
    2. using System.Linq;
    3. using System.Collections.Generic;
    4. using System.Security.Cryptography;
    5.  
    6. // Taken from http://www.superstarcoders.com/blogs/posts/md4-hash-algorithm-in-c-sharp.aspx
    7. // Probably not the best implementation of MD4, but it works.
    8. public class MD4 : HashAlgorithm
    9. {
    10.    private uint _a;
    11.    private uint _b;
    12.    private uint _c;
    13.    private uint _d;
    14.    private uint[] _x;
    15.    private int _bytesProcessed;
    16.  
    17.    public MD4()
    18.    {
    19.       _x = new uint[16];
    20.  
    21.       Initialize();
    22.    }
    23.  
    24.    public override void Initialize()
    25.    {
    26.       _a = 0x67452301;
    27.       _b = 0xefcdab89;
    28.       _c = 0x98badcfe;
    29.       _d = 0x10325476;
    30.  
    31.       _bytesProcessed = 0;
    32.    }
    33.  
    34.    protected override void HashCore(byte[] array, int offset, int length)
    35.    {
    36.       ProcessMessage(Bytes(array, offset, length));
    37.    }
    38.  
    39.    protected override byte[] HashFinal()
    40.    {
    41.       try
    42.       {
    43.          ProcessMessage(Padding());
    44.  
    45.          return new [] {_a, _b, _c, _d}.SelectMany(word => Bytes(word)).ToArray();
    46.       }
    47.       finally
    48.       {
    49.          Initialize();
    50.       }
    51.    }
    52.  
    53.    private void ProcessMessage(IEnumerable<byte> bytes)
    54.    {
    55.       foreach (byte b in bytes)
    56.       {
    57.          int c = _bytesProcessed & 63;
    58.          int i = c >> 2;
    59.          int s = (c & 3) << 3;
    60.    
    61.          _x[i] = (_x[i] & ~((uint)255 << s)) | ((uint)b << s);
    62.    
    63.          if (c == 63)
    64.          {
    65.             Process16WordBlock();
    66.          }
    67.    
    68.          _bytesProcessed++;
    69.       }
    70.    }
    71.  
    72.    private static IEnumerable<byte> Bytes(byte[] bytes, int offset, int length)
    73.    {
    74.       for (int i = offset; i < length; i++)
    75.       {
    76.          yield return bytes[i];
    77.       }
    78.    }
    79.  
    80.    private IEnumerable<byte> Bytes(uint word)
    81.    {
    82.       yield return (byte)(word & 255);
    83.       yield return (byte)((word >> 8) & 255);
    84.       yield return (byte)((word >> 16) & 255);
    85.       yield return (byte)((word >> 24) & 255);
    86.    }
    87.  
    88.    private IEnumerable<byte> Repeat(byte value, int count)
    89.    {
    90.       for (int i = 0; i < count; i++)
    91.       {
    92.          yield return value;
    93.       }
    94.    }
    95.  
    96.    private IEnumerable<byte> Padding()
    97.    {
    98.       return Repeat(128, 1)
    99.          .Concat(Repeat(0, ((_bytesProcessed + 8) & 0x7fffffc0) + 55 - _bytesProcessed))
    100.          .Concat(Bytes((uint)_bytesProcessed << 3))
    101.          .Concat(Repeat(0, 4));
    102.    }
    103.  
    104.    private void Process16WordBlock()
    105.    {
    106.       uint aa = _a;
    107.       uint bb = _b;
    108.       uint cc = _c;
    109.       uint dd = _d;
    110.  
    111.       foreach (int k in new [] { 0, 4, 8, 12 })
    112.       {
    113.          aa = Round1Operation(aa, bb, cc, dd, _x[k], 3);
    114.          dd = Round1Operation(dd, aa, bb, cc, _x[k + 1], 7);
    115.          cc = Round1Operation(cc, dd, aa, bb, _x[k + 2], 11);
    116.          bb = Round1Operation(bb, cc, dd, aa, _x[k + 3], 19);
    117.       }
    118.  
    119.       foreach (int k in new [] { 0, 1, 2, 3 })
    120.       {
    121.          aa = Round2Operation(aa, bb, cc, dd, _x[k], 3);
    122.          dd = Round2Operation(dd, aa, bb, cc, _x[k + 4], 5);
    123.          cc = Round2Operation(cc, dd, aa, bb, _x[k + 8], 9);
    124.          bb = Round2Operation(bb, cc, dd, aa, _x[k + 12], 13);
    125.       }
    126.  
    127.       foreach (int k in new [] { 0, 2, 1, 3 })
    128.       {
    129.          aa = Round3Operation(aa, bb, cc, dd, _x[k], 3);
    130.          dd = Round3Operation(dd, aa, bb, cc, _x[k + 8], 9);
    131.          cc = Round3Operation(cc, dd, aa, bb, _x[k + 4], 11);
    132.          bb = Round3Operation(bb, cc, dd, aa, _x[k + 12], 15);
    133.       }
    134.  
    135.       unchecked
    136.       {
    137.          _a += aa;
    138.          _b += bb;
    139.          _c += cc;
    140.          _d += dd;
    141.       }
    142.    }
    143.  
    144.    private static uint ROL(uint value, int numberOfBits)
    145.    {
    146.       return (value << numberOfBits) | (value >> (32 - numberOfBits));
    147.    }
    148.  
    149.    private static uint Round1Operation(uint a, uint b, uint c, uint d, uint xk, int s)
    150.    {
    151.       unchecked
    152.       {
    153.          return ROL(a + ((b & c) | (~b & d)) + xk, s);
    154.       }
    155.    }
    156.  
    157.    private static uint Round2Operation(uint a, uint b, uint c, uint d, uint xk, int s)
    158.    {
    159.       unchecked
    160.       {
    161.          return ROL(a + ((b & c) | (b & d) | (c & d)) + xk + 0x5a827999, s);
    162.       }
    163.    }
    164.  
    165.    private static uint Round3Operation(uint a, uint b, uint c, uint d, uint xk, int s)
    166.    {
    167.       unchecked
    168.       {
    169.          return ROL(a + (b ^ c ^ d) + xk + 0x6ed9eba1, s);
    170.       }
    171.    }
    172. }
    173.  
    174. public static class FileIDUtil
    175. {
    176.     public static int Compute(Type t)
    177.     {
    178.         string toBeHashed = "s\0\0\0" + t.Namespace + t.Name;
    179.  
    180.         using (HashAlgorithm hash = new MD4())
    181.         {
    182.             byte[] hashed = hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(toBeHashed));
    183.  
    184.             int result = 0;
    185.  
    186.             for(int i = 3; i >= 0; --i)
    187.             {
    188.                 result <<= 8;
    189.                 result |= hashed[i];
    190.             }
    191.  
    192.             return result;
    193.         }
    194.     }
    195. }
     
  3. Lambda Knight

    Lambda Knight

    Joined:
    Mar 8, 2011
    Posts:
    23
    Also, since you mentioned writing a Ruby script, here's the same thing in Ruby:

    Code (csharp):
    1. require("openssl")
    2. def getFileID(namespace, name)
    3.     s = "s\0\0\0"+namespace+name
    4.     return OpenSSL::Digest.digest("MD4", s)[0..3].unpack('l<').first
    5. end
     
  4. genail

    genail

    Joined:
    Jul 2, 2012
    Posts:
    1,023
    How... do... you... know... this...?!
    That's just great!!! Thank you!
     
    Last edited: Jul 11, 2014
  5. Lambda Knight

    Lambda Knight

    Joined:
    Mar 8, 2011
    Posts:
    23
    Short Story: Used dtrace to figure out which calls were being made to libssl.dylib and which arguments were being passed.

    Longer Story: First, I figured out what that value is in binary and then searched the binaries in the Library folder for that value to figure out where it was contained. It shows up in assetDatabase3 and in the individual file for the DLL in the Library/metadata folder.
    Then I used Instruments' File I/O instrument to figure out which functions were being called when those files were written. Figured that out and then used the Sampler to get a more complete view of what was being called around that function. Finally found a function called AssetImporter::GenerateFileIDHashBased.

    Used lldb to step through that function assembly instruction by assembly instruction and eventually found that it was making a call to MD4_Update in libssl. So, it was based on MD4, so, I quickly just tried shoving "<namespace>+<name>" into MD4, but I didn't see anything that looked like it was related to the fileID.

    So then I made a custom dtrace instrument which just probed all calls to MD4_Update and displayed their arguments. I ran Unity with the dtrace probes and found that it was calling MD4_Update with the int 0x73 ("s\0\0\0" as a string) and then MD4_Update with the string "<namespace>+<name>". So, I ran that through MD4 and noticed that the first four bytes were the fileID (in little endian byte order).
     
    LW, JVLVince, TMPxyz and 4 others like this.
  6. luzac

    luzac

    Joined:
    Jul 11, 2014
    Posts:
    2
    Thank you for sharing this amazing thing!
     
  7. genail

    genail

    Joined:
    Jul 2, 2012
    Posts:
    1,023
    OK this is not yet finished, but I will post it anyway.
    Here's my toolbox: https://github.com/genail/genail-toolbox

    It consists of several scripts:
    gt-fileid - computes fileid using the script above (and it works!)
    gt-genguid - for guid generation
    gt-regenguids - replaces all meta files guids in chosen directory and fixes references (good for duplicating)
    gt-replaces - replace strings in sources. You can specify a configuration file
    gt-update-references - Replaces recursively references to resource with another (guid and fileid)
    gt-unitymake - utilizes all the rest and creates a simple build script

    It's not finished. It has some hard coded paths (Windows Unity install location), and documentation is not valid in some places, but it can be used!

    Here's an example ReplaceFile.rb that has been used to build Mad Level Manager trial version: http://pastebin.com/GGW0k3ce
     
  8. DomDom

    DomDom

    Joined:
    Oct 18, 2013
    Posts:
    32
    Thank you for posting the md4 script. I'm trying to recreate the "m_localIdentifiertInFile" which I think is the id at the bottom not directly the fileid:
    Transform:
    m_ObjectHideFlags: 0
    m_PrefabParentObject: {fileID: 400018, guid: 9f2fa551b27e9824094b74d1028e77fa, type: 2}
    m_PrefabInternal: {fileID: 265297395}
    m_GameObject: {fileID: 18793384}
    m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
    m_LocalPosition: {x: .149999976, y: -.099999994, z: 0}
    m_LocalScale: {x: 1, y: 1, z: 1}
    m_Children: []
    m_Father: {fileID: 93346996}
    m_RootOrder: 0
    --- !u!114 &18793387
    But I can't tell yet what I need for it. ' "s\0\0\0" + t.Namespace + t.Name; ' doesn't seem to be all I need. Any hints?
     
    thienhaflash likes this.
  9. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
    Note that the initial "s\0\0\0" is actually 115 (as a 32-bit integer), which is the class ID for MonoScript. That can't be coincidence. :)

     
  10. TMPxyz

    TMPxyz

    Joined:
    Jul 20, 2012
    Posts:
    745
    Does this algorithm still hold?

    I find that the fileID of a "Test Component" from UnityTestTools are different when in loose scripts and in an external DLL on Unity5.5.0f3.

    Did Unity change how they calculate the Id or I got some steps wrong here?

    ====== update ======

    After some verification, this algorithm still works for external DLL on Unity5.5.0f3;
     
    Last edited: Jan 12, 2017
  11. xakzhong

    xakzhong

    Joined:
    Sep 25, 2014
    Posts:
    12
    LW likes this.
  12. jocyf

    jocyf

    Joined:
    Jan 30, 2007
    Posts:
    231
  13. jocyf

    jocyf

    Joined:
    Jan 30, 2007
    Posts:
    231
    I needed to change a project from Dll to source code and it just can't work without searching & replacing all GUID and FileID. I ended up writing a little script to do that inside Unity from the menu.

    You write in the script itself the original and final GUID and FileID (lines 36, 37, 39 & 40) and run it from menu->Tools->Regenerate asset GUIDs.
    In theory you can write more than one GUID and File ID, but I haven't tested.

    I use it to change 2 GUIDs in very, very large and complicated project without any problem.

    To know how to look at the GUID you want to replace (original and final) look at here. (Step 3):
    http://forum.unity3d.com/threads/14...-recompiling?p=1026639&viewfull=1#post1026639


    Backup your project first, It perfectly can break your project for good.!!!!!


    Code (CSharp):
    1.  
    2. /*
    3.  
    4. BACK UP YOUR PROJECT
    5.  
    6. */
    7.  
    8.  
    9. using System;
    10. using System.Collections.Generic;
    11. using System.IO;
    12. using UnityEditor;
    13. using UnityEngine;
    14.  
    15. public class UnityGuidRegeneratorMenu : MonoBehaviour {
    16.     [MenuItem("Tools/Regenerate asset GUIDs")]
    17.     public static void RegenerateGuids() {
    18.         if (EditorUtility.DisplayDialog("GUIDs regeneration",
    19.             "You are going to start the process of GUID regeneration. This may have unexpected results. \n\n MAKE A PROJECT BACKUP BEFORE PROCEEDING!",
    20.             "Regenerate GUIDs", "Cancel")) {
    21.             try {
    22.                 AssetDatabase.StartAssetEditing();
    23.                 string path = Path.GetFullPath(".") + Path.DirectorySeparatorChar + "Assets";
    24.                 RegenerateGuids (path);
    25.             }
    26.             finally {
    27.                 AssetDatabase.StopAssetEditing();
    28.                 EditorUtility.ClearProgressBar();
    29.                 AssetDatabase.Refresh();
    30.             }
    31.         }
    32.     }
    33.  
    34.     private static readonly string[] fileListPath = { "*.meta", "*.mat", "*.anim", "*.prefab", "*.unity", "*.asset" };
    35.  
    36.     static string[] oldGUIDsList = new string[1] { "74dfce233ddb29b4294c3e23c1d3650d" };
    37.     static string[] newGUIDsList = new string[1] { "89f0137620f6af44b9ba852b4190e64e" };
    38.  
    39.     static string[] oldFileIDsList = new string[1] { "11500000" };
    40.     static string[] newFileIDsList = new string[1] { "-667331979" };
    41.  
    42.     static  string _assetsPath;
    43.     static Dictionary<string, string> GUIDDict = new Dictionary<string, string>();
    44.     static Dictionary<string, string> FileIDDict = new Dictionary<string, string>();
    45.  
    46.     public static void RegenerateGuids(string path) {
    47.         //Debug.Log ("Init.");
    48.         for(int i = 0; i < oldGUIDsList.Length; i++)
    49.             GUIDDict.Add(oldGUIDsList[i], newGUIDsList[i]);
    50.  
    51.         for(int i = 0; i < oldFileIDsList.Length; i++)
    52.             FileIDDict.Add(oldFileIDsList[i], newFileIDsList[i]);
    53.  
    54.          //Get the list of files to modify
    55.         _assetsPath = path;
    56.         Debug.Log ("Read File List: "+ _assetsPath);
    57.         //string[] fileList = File.ReadAllLines(_assetsPath + fileListPath);
    58.         // Get list of working files
    59.         List<string> fileList = new List<string>();
    60.         foreach (string extension in fileListPath) {
    61.             fileList.AddRange( Directory.GetFiles(_assetsPath, extension, SearchOption.AllDirectories) );
    62.         }
    63.  
    64.         //Debug.Log ("GUI Start for each");
    65.         foreach (string f in fileList) {
    66.             //Debug.Log ("file: " + f);
    67.             string[] fileLines = File.ReadAllLines( f );
    68.        
    69.              for(int i = 0; i < fileLines.Length; i++) {
    70.                 bool GUIReplaced = false;
    71.                 //find all instances of the string "guid: " and grab the next 32 characters as the old GUID
    72.                 if(fileLines[i].Contains("guid: ")) {
    73.                      int index = fileLines[i].IndexOf("guid: ") + 6;
    74.                      string oldGUID = fileLines[i].Substring(index, 32); // GUID has 32 characters.
    75.                      //use that as a key to the dictionary and find the value
    76.                      //replace those 32 characters with the new GUID value
    77.                      if(GUIDDict.ContainsKey(oldGUID)) {
    78.                         fileLines[i] = fileLines[i].Replace(oldGUID, GUIDDict[oldGUID]);
    79.                         GUIReplaced = true;
    80.                         Debug.Log("replaced GUID \"" + oldGUID + "\" with \"" + GUIDDict[oldGUID] + "\" in file " + f);
    81.                      }
    82.                     //else Debug.Log("GUIDDict did not contain the key " + oldGUID);
    83.                 }
    84.  
    85.                 if (GUIReplaced && fileLines [i].Contains ("fileID: ")) {
    86.                     int index = fileLines[i].IndexOf("fileID: ") + 8;
    87.                     int index2 = fileLines[i].IndexOf(",", index);
    88.                     string oldFileID = fileLines[i].Substring(index, index2-index); // GUID has 32 characters.
    89.                     //Debug.Log("FileID: "+oldFileID);
    90.                     //use that as a key to the dictionary and find the value
    91.                     //replace those 32 characters with the new GUID value
    92.                     if(FileIDDict.ContainsKey(oldFileID)) {
    93.                         fileLines[i] = fileLines[i].Replace(oldFileID, FileIDDict[oldFileID]);
    94.                         Debug.Log("replaced FileID \"" + oldFileID + "\" with \"" + FileIDDict[oldFileID] + "\" in file " + f);
    95.                     }
    96.                     //else Debug.Log("FileIDDict did not contain the key " + oldFileID);
    97.                 }
    98.              }
    99.              //Write the lines back to the file
    100.             File.WriteAllLines(f, fileLines);
    101.          }
    102.      }
    103.  
    104. }
    It took my a lot of time to understand what and how to do it and make the script to make it work .
    I hope it could be useful to somebody.
     
    Last edited: Aug 29, 2017
  14. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    1,251
    Do the .meta, .mat and .anim files need to be replaced? I've written my own solution replacing .prefab, .unity and .asset files only. As far as I've experienced, the files that need to have their references replaced are those that can store a reference to a MonoBehaviour or ScriptableObject only.
     
  15. jocyf

    jocyf

    Joined:
    Jan 30, 2007
    Posts:
    231
    Depends on what references do actually change when using some dll based utility. In my case, the material references do change when using dll or src, so I had included those too. It was a utility that actually DO create & use their own materials and textures.
    In my case not doing that result in not having my materials updated (there was no materals at all). I suposse is not mandatory in all cases...

    Saludos desde Donosti.
    Si tienes cualquier problema, coméntamelo; me costó Dios y ayuda entender y sacar adelante esta historia de cambiar una versión con src por otra con dll (o viceversa). email: jocyf@jocyf.com
     
  16. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    100
    Under what conditions does the editor assign a file ID (in 5.6)? I've noticed that after saving a scene, some components of scene objects get them and others do not. Transforms often get them, but not always.
     
  17. jocyf

    jocyf

    Joined:
    Jan 30, 2007
    Posts:
    231
    The change always happens when you instead using a script you change it with a dll with that script in it (or viceversa).
    On the Transform component case, I don't know because that script vs Dll "change" cannot be done.
     
  18. s_guy

    s_guy

    Joined:
    Feb 27, 2013
    Posts:
    100
    Sorry, I guess I was a bit off topic. I was just asking about the normal Unity behavior for assigning a File ID. Sometimes a component on a scene object still has this set to 0 after saving the scene. The rules for when one gets assigned aren't obvious to me.
     
  19. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    1,251
    ¡Gracias! Te contesto en inglés, para que se pueda leer en el foro.

    Many thanks! I've also came up with a solution myself that allows me to exchange DLL with source code (and opposite). First, I parse the .cs files looking for MonoBehaviour-derived classes, grabbing their GUIDs and computing the corresponding FileID. The result is written to a CSV file, which contains the name, GUID and FileID per each component. Using that file I can then search the project files for GUID references and replace them with the corresponding FileID. The reverse procedure is straightforward.
     
    jocyf likes this.
  20. ctaggart

    ctaggart

    Joined:
    Dec 7, 2017
    Posts:
    4
    Thanks for the solution. Here is an F# function using MD4 from https://www.nuget.org/packages/HashLib/ that I got working thanks to this thread. \000 is null character in F# instead of just \0 in C#.

    Code (FSharp):
    1. open System
    2.  
    3. let md4 = HashLib.HashFactory.Crypto.CreateMD4()
    4.  
    5. let computeFileID (tp: Type): int =
    6.     // https://forum.unity.com/threads/yaml-fileid-hash-function-for-dll-scripts.252075/
    7.     let hash =
    8.         sprintf "s\000\000\000%s%s" tp.Namespace tp.Name
    9.         |> Text.Encoding.UTF8.GetBytes
    10.         |> md4.ComputeBytes
    11.     BitConverter.ToInt32(hash.GetBytes(), 0)
     
    Dmitriy-Yukhanov and jocyf like this.
  21. Mikilo

    Mikilo

    Joined:
    Jan 29, 2013
    Posts:
    362
    I know I am late to the party, but I wrote this free package to recover broken references.
    https://assetstore.unity.com/packages/tools/utilities/ng-missing-script-recovery-102272

    I don't use a map and it is very straightforward.

    You just scan your project and it will fix your issues automatically.

    The algorithm relies on fields data in the file, to match potential types (Including DLL or plain code).
    Therefore, this plugin works for 99% cases.
     
    JimChan88 likes this.
  22. yoyobbi

    yoyobbi

    Joined:
    Nov 26, 2013
    Posts:
    15
    Also note that as of Unity 2017 (I think?) there is now a ".info" file created in the Library\metadata folder for each external assembly, which lists each class in the assembly along with it's "localIdentifier" (i.e. file ID).
     
    Mikilo likes this.
  23. StephenHodgson-Valorem

    StephenHodgson-Valorem

    Joined:
    Mar 8, 2017
    Posts:
    127
    Hey guys, I was looking into this as well and realized you don't need to manually calculate the fileIds. Just let the editor do it for you.

    As long as you have the dll asset path, you can load all the UnityEngine.Objects using that path, then use AssetDataBase.TryGetGUIDAndLocalFileIdentifier to get all the info you need.

    Here's a full snippet:

    Code (CSharp):
    1.  
    2. string dllGuid = null;
    3. var assemblyObjects = AssetDatabase.LoadAllAssetsAtPath(assemblyPath.GetUnityProjectRelativePath());
    4.  
    5. for (var i = 0; i < assemblyObjects.Length; i++)
    6. {
    7.     long dllFileId;
    8.  
    9.     if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(assemblyObjects[i], out dllGuid, out dllFileId))
    10.     {
    11.         Debug.Log(assemblyObjects[i].name + " " + dllFileId.ToString());
    12.     }
    13. }
    14.  
     
    Last edited: Dec 4, 2018
    JVLVince likes this.
  24. Mikilo

    Mikilo

    Joined:
    Jan 29, 2013
    Posts:
    362
    That is true, but unfortunately it has been introduced very recently.

    You don't have this method in old Unity.
     
    JVLVince likes this.
  25. AFrisby

    AFrisby

    Joined:
    Apr 14, 2010
    Posts:
    210
    Has anyone figured this out for non-script assets?