예제 #1
0
        public static void Compile(string script, string outputPath, Platform platform, bool includePDB)
        {
            const string defaultModuleName = "AtsCallConverter00000000000000000000000000000000";
            var          randomModuleName  = "AtsCallConverter" + Guid.NewGuid().ToString("N");
            // Prevent module name conflicts
            var tempOutputPath = Path.Combine(Path.GetDirectoryName(outputPath),
                                              "BlocklyAtsGenerated-" + Guid.NewGuid() + ".dll");
            var modifiedProxyDllPath = Path.Combine(Path.GetDirectoryName(outputPath), randomModuleName + ".dll");
            var sourceCode           = CombineCode(script, platform);

            var settings = new Dictionary <string, string>()
            {
                { "CompilerVersion", "v4.0" }
            };

            string[] commonReferences = new string[] {
                "mscorlib.dll",
                "System.Core.dll",
                "System.dll",
                "Microsoft.CSharp.dll",
                "System.Windows.Forms.dll"
            };

            try {
                CSharpCodeProvider codeProvider = new CSharpCodeProvider(settings);
                CompilerParameters parameters   = new CompilerParameters()
                {
                    IncludeDebugInformation = includePDB,
                    GenerateExecutable      = false,
                    OutputAssembly          = tempOutputPath,
                };
                if (!includePDB)
                {
                    parameters.CompilerOptions += "/optimize";
                }

                foreach (string a in commonReferences)
                {
                    parameters.ReferencedAssemblies.Add(a);
                }
                if (platform == Platform.OpenBve)
                {
                    parameters.ReferencedAssemblies.Add(Path.Combine(PlatformFunction.AppDir, "lib", "OpenBveApi.dll"));
                }
                else if (platform == Platform.WinDll32 || platform == Platform.WinDll64)
                {
                    var boilerplateFile = Path.Combine(
                        PlatformFunction.AppDir,
                        "lib",
                        platform == Platform.WinDll32 ? "AtsCallConverter_x86.dll" : "AtsCallConverter_x64.dll"
                        );
                    byte[] srcBytes       = File.ReadAllBytes(boilerplateFile);
                    byte[] classNameBytes = Encoding.ASCII.GetBytes(randomModuleName);

                    // You need to use different module names for each plugin.
                    // Otherwise, the type references will mess up when multiple plugins are loaded by DetailManager.
                    ReplaceBytes(ref srcBytes, Encoding.ASCII.GetBytes(defaultModuleName),
                                 Encoding.ASCII.GetBytes(randomModuleName));
                    ReplaceBytes(ref srcBytes, Encoding.Unicode.GetBytes(defaultModuleName),
                                 Encoding.Unicode.GetBytes(randomModuleName));
                    File.WriteAllBytes(modifiedProxyDllPath, srcBytes);
                    parameters.ReferencedAssemblies.Add(modifiedProxyDllPath);
                }

                CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, sourceCode);
                if (results.Errors.HasErrors)
                {
                    throw new CompileException(results.Errors);
                }

                if (File.Exists(outputPath))
                {
                    File.Delete(outputPath);
                }
                if (platform == Platform.OpenBve)
                {
                    File.Copy(tempOutputPath, outputPath);
                    if (includePDB)
                    {
                        var targetPdbFile = Path.ChangeExtension(outputPath, ".pdb");
                        if (File.Exists(targetPdbFile))
                        {
                            File.Delete(targetPdbFile);
                        }
                        File.Move(Path.ChangeExtension(tempOutputPath, ".pdb"), targetPdbFile);
                    }
                }
                else if (platform == Platform.WinDll32 || platform == Platform.WinDll64)
                {
                    var programData = new FileStream(tempOutputPath, FileMode.Open, FileAccess.Read);
                    var proxyStream = new FileStream(modifiedProxyDllPath, FileMode.Open, FileAccess.Read);
                    var outStream   = new FileStream(outputPath, FileMode.Create, FileAccess.Write);

                    // Write Identifier and PE length to DOS stub
                    byte[] identifier = Encoding.UTF8.GetBytes("BATSNET1");
                    proxyStream.CopySectionTo(outStream, 0x6C - identifier.Length);
                    outStream.Write(identifier, 0, identifier.Length);
                    outStream.Write(BitConverter.GetBytes(proxyStream.Length), 0, 4);
                    outStream.Write(BitConverter.GetBytes(programData.Length), 0, 4);
                    proxyStream.Seek(8 + identifier.Length, SeekOrigin.Current);
                    proxyStream.CopyTo(outStream);
                    proxyStream.Close();

                    programData.CopyTo(outStream);
                    programData.Close();
                    if (includePDB)
                    {
                        var pdbFilePath = Path.ChangeExtension(tempOutputPath, ".pdb");
                        var pdbStream   = new FileStream(pdbFilePath, FileMode.Open, FileAccess.Read);
                        pdbStream.CopyTo(outStream);
                        pdbStream.Close();
                        File.Delete(pdbFilePath);
                    }
                    outStream.Close();
                }
            } finally {
                if (modifiedProxyDllPath != null)
                {
                    File.Delete(modifiedProxyDllPath);
                }
                if (tempOutputPath != null)
                {
                    File.Delete(tempOutputPath);
                }
            }
        }