Skip to content

Instantly share code, notes, and snippets.

@cnlohr
Last active November 24, 2025 06:16
Show Gist options
  • Select an option

  • Save cnlohr/ec97802efda7974227178b85520731d6 to your computer and use it in GitHub Desktop.

Select an option

Save cnlohr/ec97802efda7974227178b85520731d6 to your computer and use it in GitHub Desktop.
Unity .net assembly visualizer
// Execute with
// gcc -g dependagen.c -o dependagen && ./dependagen > test.dot
// dot -Tpng < test.dot > test.png
#include <stdint.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#define CNRBTREE_IMPLEMENTATION
#include "cnrbtree.h"
#define IGNORE_UNITY_BASE
typedef struct assembly_t
{
char ** dependencies;
char * name;
char * guid;
} assembly;
typedef char * str;
CNRBTREETEMPLATE( str, assembly, RBstrcmp, RBstrcpy, RBstrdel );
cnrbtree_strassembly * deps;
int chainlen( char ** ptr )
{
int len = 0;
while( ptr[len] ) len++;
return len;
}
char ** pushstr( char ** ptr, char * data )
{
int cl = chainlen( ptr );
char * dup = strdup( data );
ptr = realloc( ptr, sizeof(char*) * (cl+2) );
ptr[cl+0] = dup;
ptr[cl+1] = 0;
return ptr;
}
char * estrrstr( char * haystack, char * needle )
{
// terrible implementation.
int lneedle = strlen( needle );
int lhaystack = strlen( haystack );
int i;
for( i = lhaystack - lneedle; i >= 0; i-- )
{
if( strncmp( haystack + i, needle, lneedle ) == 0 ) return haystack + i;
}
return 0;
}
char * strrev( char * str, int cnt )
{
int len = strlen(str);
if( len <= cnt ) return "";
return str + len - cnt;
}
void enumerate( const char * dirName )
{
DIR* dir = opendir( dirName );
struct dirent *pent = NULL;
if( dir == 0 )
{
fprintf( stderr, "Warning: Could not explore %s errno: %s\n", dirName, strerror(errno) );
return;
}
while (pent = readdir (dir))
{
if ( pent->d_name[0] == '.' )
continue;
char * fileName = (char*)malloc( strlen(dirName) + strlen(pent->d_name) + 2 );
strcpy( fileName, dirName );
strcat( fileName, "/" );
strcat( fileName, pent->d_name );
struct stat st;
stat(fileName, &st);
if( S_ISDIR( st.st_mode ) )
{
enumerate( fileName );
}
else
{
if( strcmp( strrev( fileName, 7 ), ".asmdef" ) == 0 )
{
char buffer[1024];
assembly a = { 0 };
char * edup = strdup( estrrstr( fileName, "/" )+1 );
*estrrstr(edup, ".asmdef") = 0;
a.name = edup;
FILE * fThis = fopen( fileName, "r" );
a.dependencies = malloc( sizeof( char* ) );
a.dependencies[0] = 0;
int in_ref = 0;
while( fgets( buffer, sizeof(buffer), fThis ) )
{
if( strncmp( buffer, " \"references\":", 17 ) == 0 )
in_ref = 1;
if( strncmp( buffer, " ],", 6 ) == 0 )
in_ref = 0;
char * ss = strstr( buffer, "\"GUID:" );
if( ss )
{
ss += 6;
char * se = strstr( ss, "\"" );
if( se )
{
*se = 0;
a.dependencies = pushstr( a.dependencies, strdup(ss) );
}
}
else if( strncmp( buffer, " \"", 9 ) == 0 && in_ref )
{
ss = buffer + 8;
ss[0] = '_';
char * se = strstr( ss, "\"" );
if( se )
{
*se = 0;
a.dependencies = pushstr( a.dependencies, strdup(ss) );
}
}
}
fclose( fThis );
char fname[1065];
snprintf( fname, sizeof(fname), "%s.meta", fileName );
FILE * fMeta = fopen( fname, "r" );
if( fMeta )
{
a.guid = 0;
while( fgets( buffer, sizeof(buffer), fMeta ) )
{
char * ss = strstr( buffer, "guid: " );
if( ss )
{
ss += 6;
char * se = strstr( ss, "\n" );
if( se )
{
*se = 0;
a.guid = strdup(ss);
}
}
}
RBA( deps, a.guid ) = a;
fclose( fMeta );
}
else
{
fprintf( stderr,"Unknown: %s\n", fname );
}
}
//fileList = realloc( fileList, (nrFiles + 1) * sizeof( char * ) );
//fileList[nrFiles++] = fileName;
}
}
closedir( dir );
}
int main()
{
deps = cnrbtree_strassembly_create();
enumerate( "." );
printf( "digraph G {\n" );
// printf( " node[shape=rect width=3 height=3]; ranksep=2;\n" );
printf( "\n\
bgcolor=\"#181818\";\n\
ranksep=2;\n\
\n\
node [\n\
fontcolor = \"#e6e6e6\",\n\
style = filled,\n\
color = \"#e6e6e6\",\n\
fillcolor = \"#333333\",\n\
shape=rect,\n\
width=3,\n\
\n\
height=3\n\
];\n\
\n\
edge [\n\
color = \"#e6e6e6\",\n\
fontcolor = \"#e6e6e6\"\n\
];\n\
");
RBFOREACH( strassembly, deps, i )
{
//printf( ".key = %s Name: %s\n", i->key, i->data.name );
int n = 0;
while( i->data.dependencies[n] )
{
char * dependThis = i->data.dependencies[n];
char * depend = 0;
if( dependThis[0] == '_' )
{
depend = dependThis+1;
}
else
{
if( dependThis && RBHAS( deps, dependThis ) )
depend = RBA( deps, dependThis ).name;
}
do
{
if( depend )
{
#ifdef IGNORE_UNITY_BASE
if( strncmp( depend, "Unity", 5 ) == 0 ) continue;
if( strncmp( depend, "Timeline", 8 ) == 0 ) continue;
if( strncmp( i->data.name, "Unity", 5 ) == 0 ) continue;
if( strncmp( i->data.name, "Timeline", 8 ) == 0 ) continue;
#endif
printf( "\"%s\" -> \"%s\";\n", i->data.name, depend );
}
} while( 0 );
n++;
}
}
printf( "}\n" );
RBDESTROY( deps );
return 0;
}
@cnlohr
Copy link
Author

cnlohr commented Nov 24, 2025

test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment