/// <summary> /// Resolves #include declarations in the file before the code is evaluated internally by MonoGame. /// </summary> /// <remarks> /// This resolves the #include declarations before the code is processed by MonoGame. This is required /// because the code is stored as a temporary file before being processed, instead of being processed at /// the source (because once we get the code, we are not processing per-file but per-asset). /// </remarks> /// <param name="assetDependencies">The asset dependencies interface to get included files from.</param> /// <param name="dirName">The folder name that this asset resides in.</param> /// <param name="code">The code to process.</param> /// <returns>The resulting code.</returns> protected async Task <string> ResolveIncludes(IAssetDependencies assetDependencies, string dirName, string code) { var regex = new Regex("^#include <([^>]+)>\\s*$", RegexOptions.IgnoreCase | RegexOptions.Multiline); var matches = regex.Matches(code).OfType <Match>().ToList(); foreach (var currentMatch in matches) { var relName = currentMatch.Groups[1].Captures[0].Value; var relComponents = relName.Split(new[] { '\\', '/' }); var dirComponents = dirName.Split('.').ToList(); for (var i = 0; i < relComponents.Length; i++) { if (relComponents[i] == ".") { // Do nothing, same directory. } else if (relComponents[i] == "..") { // Parent directory. if (dirComponents.Count > 0) { dirComponents.RemoveAt(dirComponents.Count - 1); } } else { dirComponents.Add(relComponents[i]); } } var includeFile = await assetDependencies.GetOptionalCompileTimeFileDependency(string.Join(".", dirComponents.ToArray())).ConfigureAwait(false); if (includeFile == null) { throw new InvalidOperationException( "Unable to include " + relName + "; resolved name " + dirComponents.ToArray() + " does not exist!"); } // TODO: Calculate original filename from the PC that this asset was sourced from/ var nameOnDisk = string.Join("\\", dirComponents.ToArray()); using (var reader = new StreamReader(await includeFile.GetContentStream().ConfigureAwait(false))) { var replace = reader.ReadToEnd(); replace = "#line 1 \"" + nameOnDisk + "\"\r\n" + replace; replace = await this.ResolveIncludes(assetDependencies, Path.GetDirectoryName(includeFile.Name.Replace(".", "/")).Replace(Path.DirectorySeparatorChar, '.'), replace).ConfigureAwait(false); code = code.Replace(currentMatch.Value, replace); } } return(code); }
public async Task CompileAsync(IAssetFsFile assetFile, IAssetDependencies assetDependencies, TargetPlatform platform, IWritableSerializedAsset output) { if (IntPtr.Size == 4) { throw new NotSupportedException("Font compilation is only supported under a 64-bit process."); } var json = string.Empty; using (var reader = new StreamReader(await assetFile.GetContentStream().ConfigureAwait(false))) { json = await reader.ReadToEndAsync().ConfigureAwait(false); } var fontDefinition = JsonConvert.DeserializeObject <FontDefinition>(json); foreach (var fontDesc in this.GetDescriptionsForAsset(fontDefinition)) { try { var manager = new PipelineManager( Environment.CurrentDirectory, Environment.CurrentDirectory, Environment.CurrentDirectory); var dictionary = new OpaqueDataDictionary(); var processor = manager.CreateProcessor("FontDescriptionProcessor", dictionary); var context = new DummyContentProcessorContext(TargetPlatformCast.ToMonoGamePlatform(platform)); var content = processor.Process(fontDesc, context); output.SetLoader <IAssetLoader <FontAsset> >(); output.SetPlatform(platform); output.SetByteArray("Data", CompileAndGetBytes(content)); output.SetString("FontName", fontDesc.FontName); output.SetFloat("FontSize", fontDesc.Size); output.SetBoolean("UseKerning", fontDesc.UseKerning); output.SetFloat("Spacing", fontDesc.Spacing); // Font compilation was successful. return; } catch (ArgumentOutOfRangeException) { // The user might not have the font installed... } catch (NullReferenceException) { // The user might not have the font installed... } } throw new InvalidOperationException("Unable to locate any font with one of the names: " + fontDefinition.FontName); }
public async Task CompileAsync(IAssetFsFile assetFile, IAssetDependencies assetDependencies, TargetPlatform platform, IWritableSerializedAsset output) { string level; using (var reader = new StreamReader(await assetFile.GetContentStream().ConfigureAwait(false))) { level = await reader.ReadToEndAsync().ConfigureAwait(false); } output.SetLoader <IAssetLoader <LevelAsset> >(); output.SetString("LevelData", level); output.SetString("LevelDataFormat", "OgmoEditor"); }
public async Task CompileAsync(IAssetFsFile assetFile, IAssetDependencies assetDependencies, TargetPlatform platform, IWritableSerializedAsset output) { TilesetJson tilesetJson; using (var stream = new StreamReader(await assetFile.GetContentStream().ConfigureAwait(false))) { tilesetJson = JsonConvert.DeserializeObject <TilesetJson>(await stream.ReadToEndAsync().ConfigureAwait(false)); } output.SetLoader <IAssetLoader <TilesetAsset> >(); output.SetString("TextureName", tilesetJson.TextureName); output.SetInt32("CellWidth", tilesetJson.CellWidth); output.SetInt32("CellHeight", tilesetJson.CellHeight); }
public async Task CompileAsync(IAssetFsFile assetFile, IAssetDependencies assetDependencies, TargetPlatform platform, IWritableSerializedAsset output) { using (var reader = new StreamReader(await assetFile.GetContentStream().ConfigureAwait(false))) { var content = await reader.ReadToEndAsync().ConfigureAwait(false); // Validate that the XML is valid so we don't throw exceptions at runtime. var document = new XmlDocument(); document.LoadXml(content); output.SetLoader <IAssetLoader <UserInterfaceAsset> >(); output.SetString("UserInterfaceFormat", "XmlVersion2"); output.SetString("UserInterfaceData", content); } }
public async Task CompileAsync(IAssetFsFile assetFile, IAssetDependencies assetDependencies, TargetPlatform platform, IWritableSerializedAsset output) { using (var content = await assetFile.GetContentStream().ConfigureAwait(false)) { // Currently we just copy the raw data. According to the XNA documentation: /* * The Stream object must point to the head of a valid PCM wave file. Also, this wave file must be in the RIFF bitstream format. * The audio format has the following restrictions: * Must be a PCM wave file * Can only be mono or stereo * Must be 8 or 16 bit * Sample rate must be between 8,000 Hz and 48,000 Hz */ output.SetLoader <IAssetLoader <AudioAsset> >(); var bytes = new byte[content.Length]; await content.ReadAsync(bytes, 0, bytes.Length).ConfigureAwait(false); output.SetByteArray("Data", bytes); } }
public async Task CompileAsync(IAssetFsFile assetFile, IAssetDependencies assetDependencies, TargetPlatform platform, IWritableSerializedAsset output) { var otherAnimations = new Dictionary <string, byte[]>(); if (assetFile.Extension != "x") { var otherFiles = (await assetDependencies.GetAvailableCompileTimeFiles()) .Where(x => x.Name.StartsWith(assetFile.Name + "-")) .ToArray(); foreach (var otherAnim in otherFiles) { using (var otherStream = await otherAnim.GetContentStream().ConfigureAwait(false)) { var b = new byte[otherStream.Length]; await otherStream.ReadAsync(b, 0, b.Length).ConfigureAwait(false); otherAnimations[otherAnim.Name.Substring((assetFile.Name + "-").Length)] = b; } } } var nameComponents = assetFile.Name.Split('.'); nameComponents[nameComponents.Length - 1] = "_FolderOptions"; var folderOptionsFile = await assetDependencies.GetOptionalCompileTimeFileDependency(string.Join(".", nameComponents)).ConfigureAwait(false); string[] importFolderOptions = null; if (folderOptionsFile != null) { using (var optionsReader = new StreamReader(await folderOptionsFile.GetContentStream().ConfigureAwait(false))) { importFolderOptions = optionsReader.ReadToEnd() .Trim() .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .Where(x => !x.StartsWith("#")) .ToArray(); } } var optionsFile = await assetDependencies.GetOptionalCompileTimeFileDependency(assetFile.Name + ".Options").ConfigureAwait(false); string[] importOptions = null; if (optionsFile != null) { using (var optionsReader = new StreamReader(await optionsFile.GetContentStream().ConfigureAwait(false))) { importOptions = optionsReader.ReadToEnd() .Trim() .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .Where(x => !x.StartsWith("#")) .ToArray(); } } if (importOptions == null) { importOptions = importFolderOptions; } byte[] modelData; using (var stream = await assetFile.GetContentStream().ConfigureAwait(false)) { modelData = new byte[stream.Length]; await stream.ReadAsync(modelData, 0, modelData.Length).ConfigureAwait(false); } var reader = new AssimpReader(_modelRenderConfigurations, _renderBatcher); var model = reader.Load(modelData, assetFile.Name, assetFile.Extension, otherAnimations, importOptions); var data = _modelSerializer.Serialize(model); output.SetLoader <IAssetLoader <ModelAsset> >(); output.SetByteArray("Data", data); }
public async Task CompileAsync(IAssetFsFile assetFile, IAssetDependencies assetDependencies, TargetPlatform platform, IWritableSerializedAsset output) { var tempPath = System.IO.Path.GetTempFileName(); try { using (var stream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None)) { using (var sourceStream = await assetFile.GetContentStream().ConfigureAwait(false)) { await sourceStream.CopyToAsync(stream).ConfigureAwait(false); } } var importer = new TextureImporter(); var monogameOutput = importer.Import(tempPath, new DummyContentImporterContext()); var originalWidth = monogameOutput.Faces[0][0].Width; var originalHeight = monogameOutput.Faces[0][0].Height; var nameComponents = assetFile.Name.Split('.'); nameComponents[nameComponents.Length - 1] = "_FolderOptions"; var folderOptionsFile = await assetDependencies.GetOptionalCompileTimeFileDependency(string.Join(".", nameComponents)).ConfigureAwait(false); string[] importFolderOptions = null; if (folderOptionsFile != null) { using (var optionsReader = new StreamReader(await folderOptionsFile.GetContentStream().ConfigureAwait(false))) { importFolderOptions = optionsReader.ReadToEnd() .Trim() .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .Where(x => !x.StartsWith("#")) .ToArray(); } } var optionsFile = await assetDependencies.GetOptionalCompileTimeFileDependency(assetFile.Name + ".Options").ConfigureAwait(false); string[] importOptions = null; if (optionsFile != null) { using (var optionsReader = new StreamReader(await optionsFile.GetContentStream().ConfigureAwait(false))) { importOptions = optionsReader.ReadToEnd() .Trim() .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .Where(x => !x.StartsWith("#")) .ToArray(); } } if (importOptions == null) { importOptions = importFolderOptions; } if (importOptions == null) { importOptions = new string[0]; } var allowResizeToPowerOfTwo = !IsPowerOfTwo((ulong)originalWidth) || !IsPowerOfTwo((ulong)originalHeight); var allowMakeSquare = originalWidth != originalHeight; var manager = new PipelineManager( Environment.CurrentDirectory, Environment.CurrentDirectory, Environment.CurrentDirectory); TextureContent content = CompileTexture(assetFile, platform, monogameOutput, importOptions, allowResizeToPowerOfTwo, allowMakeSquare, manager); Console.WriteLine("Texture " + assetFile.Name + " resized " + originalWidth + "x" + originalHeight + " -> " + content.Faces[0][0].Width + "x" + content.Faces[0][0].Height); output.SetLoader <IAssetLoader <TextureAsset> >(); output.SetPlatform(platform); output.SetByteArray("Data", CompileAndGetBytes(content)); output.SetInt32("OriginalWidth", originalWidth); output.SetInt32("OriginalHeight", originalHeight); } finally { try { File.Delete(tempPath); } catch { } } }
public async Task CompileAsync(IAssetFsFile assetFile, IAssetDependencies assetDependencies, TargetPlatform platform, IWritableSerializedAsset output) { var code = string.Empty; using (var reader = new StreamReader(await assetFile.GetContentStream().ConfigureAwait(false))) { code = await reader.ReadToEndAsync().ConfigureAwait(false); } if (!code.Contains("// uber")) { // Do nothing with this file. return; } var dirName = Path.GetDirectoryName(assetFile.Name.Replace(".", "/")); code = await ResolveIncludes(assetDependencies, dirName.Replace(Path.DirectorySeparatorChar, '.'), code).ConfigureAwait(false); var allPassed = true; var effectCodes = new Dictionary <string, Tuple <string, byte[], byte[]> >(); foreach (var rawLine in code.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)) { var line = rawLine.Trim(); if (line.StartsWith("#line")) { continue; } if (!line.StartsWith("// uber ")) { break; } var components = line.Substring("// uber ".Length).Split(':'); var name = components[0].Trim(); var defines = components[1].Trim(); Console.WriteLine(); Console.Write("Compiling uber shader variant " + name + "... "); var effectOutput = new EffectContent(); effectOutput.EffectCode = this.GetEffectPrefixCode() + code; string tempPath = null, tempOutputPath = null; try { tempPath = Path.GetTempFileName(); tempOutputPath = Path.GetTempFileName(); using (var writer = new StreamWriter(tempPath)) { writer.Write(effectOutput.EffectCode); } effectOutput.Identity = new ContentIdentity(tempPath); var debugContent = EffectCompilerHelper.Compile( effectOutput, tempOutputPath, platform, true, defines); var releaseContent = EffectCompilerHelper.Compile( effectOutput, tempOutputPath, platform, false, defines); effectCodes[name] = new Tuple <string, byte[], byte[]>(defines, debugContent.GetEffectCode(), releaseContent.GetEffectCode()); Console.Write("done."); } catch (InvalidContentException ex) { Console.WriteLine("failed."); Console.Write(ex.Message.Trim()); allPassed = false; } finally { if (tempOutputPath != null) { File.Delete(tempOutputPath); } if (tempOutputPath != null) { File.Delete(tempPath); } } } Console.WriteLine(); Console.Write("Finalizing uber shader compilation... "); if (!allPassed) { throw new Exception("One or more uber shader variants failed to compile (see above!)"); } using (var memory = new MemoryStream()) { using (var writer = new BinaryWriter(memory)) { writer.Write((uint)2); writer.Write((uint)effectCodes.Count); foreach (var kv in effectCodes) { writer.Write(kv.Key); writer.Write(kv.Value.Item1); writer.Write(kv.Value.Item2.Length); writer.Write(kv.Value.Item2); writer.Write(kv.Value.Item3.Length); writer.Write(kv.Value.Item3); } var len = memory.Position; var data = new byte[len]; memory.Seek(0, SeekOrigin.Begin); memory.Read(data, 0, data.Length); output.SetLoader <IAssetLoader <UberEffectAsset> >(); output.SetPlatform(platform); output.SetByteArray("Data", data); } } }
public async Task CompileAsync(IAssetFsFile assetFile, IAssetDependencies assetDependencies, TargetPlatform platform, IWritableSerializedAsset output) { var code = string.Empty; using (var reader = new StreamReader(await assetFile.GetContentStream().ConfigureAwait(false))) { code = await reader.ReadToEndAsync().ConfigureAwait(false); } if (code.Contains("// uber")) { // Do nothing with this file. return; } var dirName = Path.GetDirectoryName(assetFile.Name.Replace(".", "/")); code = await ResolveIncludes(assetDependencies, dirName.Replace(Path.DirectorySeparatorChar, '.'), code).ConfigureAwait(false); var effectContent = new EffectContent(); effectContent.EffectCode = this.GetEffectPrefixCode() + code; var tempPath = Path.GetTempFileName(); using (var writer = new StreamWriter(tempPath)) { writer.Write(effectContent.EffectCode); } effectContent.Identity = new ContentIdentity(tempPath); var tempOutputPath = Path.GetTempFileName(); var debugContent = EffectCompilerHelper.Compile( effectContent, tempOutputPath, platform, true, string.Empty); var releaseContent = EffectCompilerHelper.Compile( effectContent, tempOutputPath, platform, false, string.Empty); using (var stream = new MemoryStream()) { using (var writer = new BinaryWriter(stream)) { // Magic flag that indicates new compiled effect format. writer.Write((uint)0x12345678); // Version 1 of new effect format. writer.Write((uint)1); var debugCode = debugContent.GetEffectCode(); var releaseCode = releaseContent.GetEffectCode(); writer.Write(debugCode.Length); writer.Write(debugCode); writer.Write(releaseCode.Length); writer.Write(releaseCode); var p = stream.Position; var b = new byte[p]; stream.Seek(0, SeekOrigin.Begin); stream.Read(b, 0, b.Length); output.SetLoader <IAssetLoader <EffectAsset> >(); output.SetPlatform(platform); output.SetByteArray("Data", b); } } File.Delete(tempPath); File.Delete(tempOutputPath); }