public static Dictionary <string, AsmCacheEntry> Load(Stream fobj) { var ret = new Dictionary <string, AsmCacheEntry>(); var header = new byte[4]; fobj.Read(header, 0, 4); if (!header.SequenceEqual(magic)) { throw new Exception("This is not an assembly cache."); } var read = new BinaryReader(fobj); var num = read.ReadInt32(); for (int i = 0; i < num; i++) { var entry = new AsmCacheEntry(); // read filename (stored as Pascal string) var flen = fobj.ReadByte(); var fbytes = new byte[flen]; fobj.Read(fbytes, 0, flen); string fname = Encoding.UTF8.GetString(fbytes); // read SHA-512 checksum fobj.Read(entry.checksum, 0, 64); // read assemblies var numasm = read.ReadInt32(); for (int j = 0; j < numasm; j++) { var asmlen = read.ReadInt32(); var asmdata = new byte[asmlen]; fobj.Read(asmdata, 0, asmlen); entry.asms.Add(asmdata); } ret.Add(fname, entry); } return(ret); }
/// <summary> /// Finds Python mods and adds them to ReflectMan's Types list. /// </summary> public static void Scan() { try { if (scanned) { throw new Exception("PythonAPI.Scan() called multiple times"); } scopes = new Dictionary <string, ScriptScope>(); var resman = new System.Resources.ResourceManager("Plex.Engine.Properties.Resources", typeof(Properties.Resources).Assembly); var provider = new CSharpCodeProvider(); var refs = AppDomain.CurrentDomain.GetAssemblies().Select(f => f.Location).Concat(new string[] { "Microsoft.CSharp.dll" }).ToArray(); var types = new List <Type>(); var sha = new SHA512Managed(); var oldcache = new Dictionary <string, AsmCacheEntry>(); var newcache = new Dictionary <string, AsmCacheEntry>(); if (File.Exists("pyasmcache.dat")) { using (var stream = File.OpenRead("pyasmcache.dat")) try { oldcache = AsmCache.Load(stream); } catch (Exception ex) { #if DEBUG Console.WriteLine("[dev] Failed to read the assembly cache."); Console.WriteLine(ex.ToString()); #endif } } foreach (var fname in Directory.GetFiles(Environment.CurrentDirectory, "*.py").Select(Path.GetFileName)) { byte[] checksum; using (FileStream stream = File.OpenRead(fname)) checksum = sha.ComputeHash(stream); var script = File.ReadAllText(fname); try { scopes[fname] = PythonHelper.RunCode(script); } catch (Exception ex) { Console.WriteLine("[dev] Failed to execute Python script " + fname); Console.WriteLine(ex.ToString()); continue; } if (oldcache.ContainsKey(fname)) { var oldentry = oldcache[fname]; if (checksum.SequenceEqual(oldentry.checksum)) { try { foreach (var asm in oldentry.asms) { types.AddRange(Assembly.Load(asm).GetTypes()); } newcache.Add(fname, oldentry); continue; } catch (Exception ex) { #if DEBUG Console.WriteLine("[dev] Failed to load cached assembly for " + fname); Console.WriteLine(ex.ToString()); #endif } } } var scriptlines = script.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n'); // line-ending independent... int pos = 0; var entry = new AsmCacheEntry(); entry.checksum = checksum; var parameters = new CompilerParameters(); parameters.ReferencedAssemblies.AddRange(refs); parameters.GenerateInMemory = false; // We need to keep the temporary file around long enough to copy it to the cache. parameters.GenerateExecutable = false; try { while (pos < scriptlines.Length) { while (!scriptlines[pos].StartsWith("#Plex")) { pos++; } var templatename = scriptlines[pos].Split(':')[1]; pos++; string decorators = ""; while (scriptlines[pos].StartsWith("#")) { decorators += scriptlines[pos].Substring(1) + Environment.NewLine; // remove # and add to string pos++; } if (!scriptlines[pos].StartsWith("class ")) { throw new Exception("Plex decorators without matching global class"); } var classname = scriptlines[pos].Split(' ')[1]; if (classname.Contains("(")) // derived class { classname = classname.Split('(')[0]; } else { classname = classname.Remove(classname.Length - 1); // remove : } var code = String.Format(resman.GetString(templatename), decorators, classname, fname.Replace("\\", "\\\\")); // generate the C# wrapper class from template #if DEBUG Console.WriteLine(code); #endif var results = provider.CompileAssemblyFromSource(parameters, code); if (results.Errors.HasErrors) { string except = "The wrapper class failed to compile."; foreach (CompilerError error in results.Errors) { except += Environment.NewLine + error.ErrorText; } throw new Exception(except); } types.AddRange(results.CompiledAssembly.GetTypes()); // We did it! entry.asms.Add(File.ReadAllBytes(results.PathToAssembly)); pos++; // keep scanning the file for more classes } } catch (Exception ex) // Skip any file that has issues { #if DEBUG Console.WriteLine("[dev] Exception in the Python API: file " + fname + ", line " + pos.ToString() + "."); Console.WriteLine(ex.ToString()); #endif } newcache.Add(fname, entry); } #if DEBUG Console.WriteLine("[dev] " + types.Count.ToString() + " Python mods loaded successfully."); #endif if (types.Count > 0) { ReflectMan.AddTypes(types.ToArray()); using (var stream = File.OpenWrite("pyasmcache.dat")) AsmCache.Save(stream, newcache); } scanned = true; } catch { } }