@@ -11,14 +11,14 @@ internal class StartupHook
1111{
1212 public static List < string > ResolveDirectories = new ( ) ;
1313
14+ public static string DoesNotExistPath = "_doesnotexist_.exe" ;
15+
1416 public static void Initialize ( )
1517 {
16- var silentExceptionLog = $ "preloader_ { DateTime . Now : yyyyMMdd_HHmmss_fff} .log";
18+ var silentExceptionLog = $ "bepinex_preloader_ { DateTime . Now : yyyyMMdd_HHmmss_fff} .log";
1719
1820 try
1921 {
20- string filename , gameDirectory ;
21-
2222//#if DEBUG
2323// filename =
2424// Path.Combine(Directory.GetCurrentDirectory(),
@@ -28,25 +28,115 @@ public static void Initialize()
2828// // for debugging within VS
2929// ResolveDirectories.Add(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
3030//#else
31- filename = Process . GetCurrentProcess ( ) . MainModule . FileName ;
32- gameDirectory = Path . GetDirectoryName ( filename ) ;
33- ResolveDirectories . Add ( Path . Combine ( gameDirectory , "BepInEx" , "core" ) ) ;
34- //#endif
31+
32+ var executableFilename = Process . GetCurrentProcess ( ) . MainModule . FileName ;
33+
34+ var assemblyFilename = TryDetermineAssemblyNameFromDotnet ( executableFilename )
35+ ?? TryDetermineAssemblyNameFromStubExecutable ( executableFilename )
36+ ?? TryDetermineAssemblyNameFromCurrentAssembly ( executableFilename ) ;
37+
38+ string gameDirectory = null ;
39+
40+ if ( assemblyFilename != null )
41+ gameDirectory = Path . GetDirectoryName ( assemblyFilename ) ;
3542
43+ string bepinexCoreDirectory = null ;
44+
45+ if ( gameDirectory != null )
46+ bepinexCoreDirectory = Path . Combine ( gameDirectory , "BepInEx" , "core" ) ;
47+
48+ if ( assemblyFilename == null || gameDirectory == null || ! Directory . Exists ( bepinexCoreDirectory ) )
49+ {
50+ throw new Exception ( "Could not determine game location, or BepInEx install location" ) ;
51+ }
52+
3653 silentExceptionLog = Path . Combine ( gameDirectory , silentExceptionLog ) ;
54+
55+ ResolveDirectories . Add ( bepinexCoreDirectory ) ;
56+ //#endif
3757
3858 AppDomain . CurrentDomain . AssemblyResolve += SharedEntrypoint . RemoteResolve ( ResolveDirectories ) ;
3959
40- NetCorePreloaderRunner . OuterMain ( filename ) ;
60+ NetCorePreloaderRunner . OuterMain ( assemblyFilename ) ;
4161 }
4262 catch ( Exception ex )
4363 {
44- File . WriteAllText ( silentExceptionLog , ex . ToString ( ) ) ;
64+ string executableLocation = null ;
65+ string arguments = null ;
66+
67+ try
68+ {
69+ executableLocation = Process . GetCurrentProcess ( ) . MainModule ? . FileName ;
70+ arguments = string . Join ( ' ' , Environment . GetCommandLineArgs ( ) ) ;
71+ }
72+ catch { }
73+
74+ string exceptionString = $ "Unhandled fatal exception\r \n " +
75+ $ "Executable location: { executableLocation ?? "<null>" } \r \n " +
76+ $ "Arguments: { arguments ?? "<null>" } \r \n " +
77+ $ "{ ex } ";
78+
79+ File . WriteAllText ( silentExceptionLog , exceptionString ) ;
4580
4681 Console . WriteLine ( "Unhandled exception" ) ;
82+ Console . WriteLine ( $ "Executable location: { executableLocation ?? "<null>" } ") ;
83+ Console . WriteLine ( $ "Arguments: { arguments ?? "<null>" } ") ;
4784 Console . WriteLine ( ex ) ;
4885 }
4986 }
87+
88+ private static string TryDetermineAssemblyNameFromDotnet ( string executableFilename )
89+ {
90+ if ( Path . GetFileNameWithoutExtension ( executableFilename ) == "dotnet" )
91+ {
92+ // We're in a special setup that uses dotnet directly to start a .dll, instead of a .exe that launches dotnet implicitly
93+
94+ var args = Environment . GetCommandLineArgs ( ) ;
95+
96+ foreach ( var arg in args )
97+ {
98+ if ( ! arg . EndsWith ( ".dll" , StringComparison . OrdinalIgnoreCase )
99+ && ! arg . EndsWith ( ".exe" , StringComparison . OrdinalIgnoreCase ) )
100+ {
101+ continue ;
102+ }
103+
104+ if ( File . Exists ( arg ) )
105+ {
106+ return Path . GetFullPath ( arg ) ;
107+ }
108+ }
109+ }
110+
111+ return null ;
112+ }
113+
114+ private static string TryDetermineAssemblyNameFromStubExecutable ( string executableFilename )
115+ {
116+ string dllFilename = Path . ChangeExtension ( executableFilename , ".dll" ) ;
117+
118+ if ( File . Exists ( dllFilename ) )
119+ return dllFilename ;
120+
121+ return null ;
122+ }
123+
124+ private static string TryDetermineAssemblyNameFromCurrentAssembly ( string executableFilename )
125+ {
126+ string assemblyLocation = typeof ( StartupHook ) . Assembly . Location . Replace ( '/' , Path . DirectorySeparatorChar ) ;
127+
128+ string coreFolderPath = Path . GetDirectoryName ( assemblyLocation ) ;
129+
130+ if ( coreFolderPath == null )
131+ return null ; // throw new Exception("Could not find a valid path to the BepInEx directory");
132+
133+ string gameDirectory = Path . GetDirectoryName ( Path . GetDirectoryName ( coreFolderPath ) ) ;
134+
135+ if ( gameDirectory == null )
136+ return null ; // throw new Exception("Could not find a valid path to the game directory");
137+
138+ return Path . Combine ( gameDirectory , DoesNotExistPath ) ;
139+ }
50140}
51141
52142namespace BepInEx . NetCore
0 commit comments