12/14/07

VB.NET XNA Content Pipeline for Game Studio 2.0

[GS 2.0]

Game Studio 2.0 beta has ended and Game Studio 2.0 has been officially released. There are breaking changes in the ContentPipeline.targets file, so I've updated the VBContentManager class to generate a Content.contentproj file that works within the new parameters. The usage of the class remains the same.

  • Works with Game Studio 2.0

  • Now outputs content to the appropriate Visual Studio bin location (i.e., bin\Debug or bin\Release, whichever you've set Visual Studio to)
Public Class VBContentManager
 Inherits ContentManager

 Public Fonts As Hashtable(Of SpriteFont)
 Public Textures As Hashtable(Of Texture2D)
 Public SubTextures As Hashtable(Of Hashtable(Of Rectangle))
 Public Sounds As Hashtable(Of SoundBank)
 Public Effects As Hashtable(Of Effect)

 Private strContentprojFile, strProjectFolder, strExecutingFolder, strContentFolder As String
 Private objContent As List(Of ContentFile)
 Private intDupeSound, intDupeTexture, intDupeFont, intDupeEffect As Integer

 Public Enum Platform
 Windows
 XBox360
 End Enum

 Public Enum ContentTypes
 Textures = 0
 Fonts = 1
 Sounds = 2
 Effects = 3
 End Enum

 Private Enum ImporterName
 TextureImporter = 0
 FontDescriptionImporter = 1
 XactImporter = 2
 EffectImporter = 3
 End Enum

 Private Enum ProcessorName
 TextureProcessor = 0
 FontDescriptionProcessor = 1
 XactProcessor = 2
 EffectProcessor = 3
 End Enum

 Public Structure ContentFile

 Public ContentType As ContentTypes
 Public File As System.IO.FileInfo
 Public Name As String

 Public Sub New(ByVal ContentType As ContentTypes, ByVal ContentFile As System.IO.FileInfo, ByVal Name As String)
 Me.ContentType = ContentType
 Me.File = ContentFile
 Me.Name = Name
 End Sub

 End Structure

 ''' <summary>
 ''' Creates a new ContentManager which has been extended with functionality to support a Visual Basic (or other non-C#) project
 ''' </summary>
 ''' <param name="ServiceProvider">Your game has a .Services property; send that in here.</param>
 ''' <param name="ContentFolder">Your content folder (a relative path, from your game's Project node as it appears in the Solution Explorer)</param>
 ''' <remarks></remarks>
 Public Sub New(ByVal ServiceProvider As IServiceProvider, ByVal ContentFolder As String)

 MyBase.New(ServiceProvider)

 With System.Reflection.Assembly.GetEntryAssembly.Location
 Me.strExecutingFolder = .Substring(0, .LastIndexOf("\"))
 End With

 With Me.strExecutingFolder
 With .Substring(0, .LastIndexOf("\") - 1)
 Me.strProjectFolder = .Substring(0, .LastIndexOf("\"))
 End With
 End With

 Me.strContentFolder = Me.strProjectFolder & "\" & ContentFolder

 Me.Fonts = New Hashtable(Of SpriteFont)
 Me.Textures = New Hashtable(Of Texture2D)
 Me.SubTextures = New Hashtable(Of Hashtable(Of Rectangle))
 Me.Sounds = New Hashtable(Of SoundBank)
 Me.Effects = New Hashtable(Of Effect)

 End Sub

 ''' <summary>
 ''' Attempts to load all assets which have been compiled by the Content Pipeline. Assets are placed into the respective Hash table (Fonts, Textures, et al)
 ''' </summary>
 ''' <remarks></remarks>
 Public Sub LoadAllContent()

 Dim objCompiledContent As New List(Of IO.FileInfo)
 Dim strUnknownFiles As String = ""

 For Each file As IO.FileInfo In New IO.DirectoryInfo(Me.strExecutingFolder).GetFiles("*.xnb", IO.SearchOption.AllDirectories)

 Try

 'Font?

 With file.FullName.ToLower
 Me.Fonts.Add(file.Name.Replace(file.Extension, ""), Me.Load(Of SpriteFont)(.Replace(Me.strExecutingFolder & "\""").Replace(file.Extension, "")))
 End With

 Catch notAFont As Exception

 Try

 'Texture2D?

 With file.FullName
 Me.Textures.Add(file.Name.Replace(file.Extension, ""), Me.Load(Of Texture2D)(.Replace(Me.strExecutingFolder & "\""").Replace(file.Extension, "")))
 End With

 'Is this a sheet w/ a rectangle map?  If so, load the rectangle map

 If IO.File.Exists(file.FullName.Replace(file.Extension, ".map")) Then
 Me.LoadMap(file.Name.Replace(file.Extension, ""), file.FullName.Replace(file.Extension, ".map"))
 End If

 Catch notATexture2D As Exception

 Try

 'Sound?

 With file.FullName.ToLower
 Me.Sounds.Add(file.Name.Replace(file.Extension, ""), Me.Load(Of SoundBank)(.Replace(Me.strExecutingFolder & "\""").Replace(file.Extension, "")))
 End With

 Catch notASoundBank As Exception

 Try

 'Effect?

 With file.FullName.ToLower
 Me.Effects.Add(file.Name.Replace(file.Extension, ""), Me.Load(Of Effect)(.Replace(Me.strExecutingFolder & "\""").Replace(file.Extension, "")))
 End With

 Catch unknown As Exception

 strUnknownFiles &= vbCrLf & file.FullName

 End Try

 End Try

 End Try

 End Try

 Next

 If strUnknownFiles <> "" Then

 MsgBox("The VBContentManager was unable to determine the correct loader for:" & vbCrLf & _
 strUnknownFiles & vbCrLf & vbCrLf & _
 "Unless loaded by something else, the above content won't be available at runtime." & vbCrLf & vbCrLf & _
 "This application may become unstable as a result.")

 End If

 End Sub

 Private Sub LoadMap(ByVal strTextureNameName As String, ByVal strMapFile As String)

 Dim objReader As IO.StreamReader
 Dim strMapData As String
 Dim strLine() As String
 Dim strItem() As String

 objReader = New IO.StreamReader(strMapFile)

 Me.SubTextures.Add(strTextureNameName, New Hashtable(Of Rectangle))

 strMapData = objReader.ReadToEnd

 objReader.Close()

 objReader.Dispose()

 strLine = strMapData.Split(vbCrLf.ToCharArray, System.StringSplitOptions.RemoveEmptyEntries)

 For Each strMapItem As String In strLine

 strItem = strMapItem.Split(",".ToCharArray, System.StringSplitOptions.RemoveEmptyEntries)

 Me.SubTextures(strTextureNameName).Add(strItem(0), New Rectangle(CInt(strItem(1)), CInt(strItem(2)), CInt(strItem(3)), CInt(strItem(4))))

 Next

 End Sub

 ''' <summary>
 ''' Compiles the VB Content Pipeline
 ''' </summary>
 ''' <param name="stealth">Hide/Show the MSBuild window during the compile process</param>
 ''' <param name="platform">If there's a way the Xbox360 will work with VB, you could pass in the Xbox parameter as a target platform to MSBuild.  But it's Windows by default.</param>
 ''' <remarks></remarks>
 Public Sub CompileContent(ByVal stealth As BooleanOptional ByVal platform As Platform = VBContentManager.Platform.Windows)

 Dim objProcess As Process
 Dim objPSI As New Diagnostics.ProcessStartInfo

 Me.objContent = New List(Of ContentFile)

 Me.HarvestContent()

 If Me.objContent.Count > 0 Then

 Me.BuildContentProj(platform)

 Try 'to write the content project file to disk

 Using TempStreamWriter As System.IO.StreamWriter = New System.IO.StreamWriter(Me.strProjectFolder & "\Content.contentproj"False)

 With TempStreamWriter

 .Write(Me.strContentprojFile)
 .Close()

 End With

 End Using

 Try

 With objPSI
 .FileName = Environment.GetEnvironmentVariable("Windir") & "\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe"
 .Arguments = """" & Me.strProjectFolder & "\Content.contentproj""" & _
 " /verbosity:normal" & _
 " /l:FileLogger,Microsoft.Build.Engine;logfile=""" & Me.strProjectFolder & "\Content.log"""
 If stealth Then
 .Arguments &= " /noconsolelogger"
 .WindowStyle = ProcessWindowStyle.Hidden
 End If
 End With

 'give the .contentproj file to MSBuild

 objProcess = System.Diagnostics.Process.Start(objPSI)

 While Not objProcess.HasExited
 Threading.Thread.Sleep(100)
 End While

 Catch ex As Exception

 MsgBox("When building the pipeline, trying to run MSBuild.exe resulted in: " & ex.Message)

 End Try

 Catch ex As Exception

 MsgBox("When building the pipeline, trying to create the Content.contentproj file """ & Me.strProjectFolder & "\Content.contentproj"" resulted in: " & ex.Message)

 End Try

 End If

 End Sub

 Private Sub HarvestContent()

 Dim objRoot As System.IO.DirectoryInfo = New System.IO.DirectoryInfo(Me.strContentFolder)

 For Each sound As System.IO.FileInfo In objRoot.GetFiles("*.xab", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Sounds, sound, sound.Name.Replace(sound.Extension, ""))) Then
 Me.intDupeSound += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Sounds, sound, sound.Name.Replace(sound.Extension, "") & Me.intDupeSound.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Sounds, sound, sound.Name.Replace(sound.Extension, "")))
 End If
 Next

 For Each texture As System.IO.FileInfo In objRoot.GetFiles("*.png", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, ""))) Then
 Me.intDupeTexture += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "") & Me.intDupeTexture.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "")))
 End If
 Next

 For Each texture As System.IO.FileInfo In objRoot.GetFiles("*.jpg", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, ""))) Then
 Me.intDupeTexture += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "") & Me.intDupeTexture.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "")))
 End If
 Next

 For Each texture As System.IO.FileInfo In objRoot.GetFiles("*.gif", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, ""))) Then
 Me.intDupeTexture += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "") & Me.intDupeTexture.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "")))
 End If
 Next

 For Each font As System.IO.FileInfo In objRoot.GetFiles("*.spritefont", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Fonts, font, font.Name.Replace(font.Extension, ""))) Then
 Me.intDupeFont += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Fonts, font, font.Name.Replace(font.Extension, "") & Me.intDupeFont.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Fonts, font, font.Name.Replace(font.Extension, "")))
 End If
 Next

 For Each effect As System.IO.FileInfo In objRoot.GetFiles("*.fx", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Effects, effect, effect.Name.Replace(effect.Extension, ""))) Then
 Me.intDupeEffect += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Effects, effect, effect.Name.Replace(effect.Extension, "") & Me.intDupeEffect.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Effects, effect, effect.Name.Replace(effect.Extension, "")))
 End If
 Next

 End Sub

 Private Sub BuildContentProj(ByVal platform As Platform)

 Dim strPlatform As String

 Select Case platform
 Case VBContentManager.Platform.XBox360
 strPlatform = "Xbox 360"
 Case Else
 strPlatform = "Windows"
 End Select

 Me.strContentprojFile = _
 "<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">" & vbCrLf & _
 vbTab & "<Import Project=""$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\v2.0\Microsoft.Xna.GameStudio.ContentPipeline.targets"" />" & vbCrLf & _
 vbTab & "<PropertyGroup>" & vbCrLf & _
 vbTab & vbTab & "<OutputType>Library</OutputType>" & vbCrLf & _
 vbTab & vbTab & "<XnaFrameworkVersion>v2.0</XnaFrameworkVersion>" & vbCrLf & _
 vbTab & vbTab & "<ProjectDir>" & Me.strProjectFolder & "\</ProjectDir>" & vbCrLf & _
 vbTab & vbTab & "<ParentOutputDir>" & Me.strExecutingFolder & "</ParentOutputDir>" & vbCrLf & _
 vbTab & vbTab & "<ContentRootDirectory>\</ContentRootDirectory>" & vbCrLf & _
 vbTab & vbTab & "<XNAContentPipelineTargetPlatform>" & strPlatform & "</XNAContentPipelineTargetPlatform>" & vbCrLf & _
 vbTab & vbTab & "<OutputPath>" & Me.strExecutingFolder & "\</OutputPath>" & vbCrLf & _
 vbTab & vbTab & "<OutputDirectory>" & Me.strExecutingFolder & "\</OutputDirectory>" & vbCrLf & _
 vbTab & "</PropertyGroup>" & vbCrLf & _
 vbTab & "<ItemGroup>" & vbCrLf & _
 vbTab & vbTab & "<Reference Include=""Microsoft.Xna.Framework.Content.Pipeline.EffectImporter, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d, processorArchitecture=MSIL"" />" & vbCrLf & _
 vbTab & vbTab & "<Reference Include=""Microsoft.Xna.Framework.Content.Pipeline.FBXImporter, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d, processorArchitecture=MSIL"" />" & vbCrLf & _
 vbTab & vbTab & "<Reference Include=""Microsoft.Xna.Framework.Content.Pipeline.TextureImporter, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d, processorArchitecture=MSIL"" />" & vbCrLf & _
 vbTab & vbTab & "<Reference Include=""Microsoft.Xna.Framework.Content.Pipeline.XImporter, Version=2.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d, processorArchitecture=MSIL"" />" & vbCrLf & _
 vbTab & "</ItemGroup>"

 For Each content As ContentFile In Me.objContent

 Me.strContentprojFile &= vbCrLf & _
 vbTab & "<ItemGroup>" & vbCrLf & _
 vbTab & vbTab & "<Compile Include=""" & content.File.FullName & """>" & vbCrLf & _
 vbTab & vbTab & vbTab & "<Name>" & content.Name & "</Name>" & vbCrLf & _
 vbTab & vbTab & vbTab & "<Importer>" & CType(content.ContentType, ImporterName).ToString & "</Importer>" & vbCrLf & _
 vbTab & vbTab & vbTab & "<Processor>" & CType(content.ContentType, ProcessorName).ToString & "</Processor>" & vbCrLf & _
 vbTab & vbTab & "</Compile>" & vbCrLf & _
 vbTab & "</ItemGroup>"

 If content.ContentType = ContentTypes.Textures Then

 'If this texture is a sheet w/ a map, copy it's map out in the executable area
 'along side the existing or eventual compiled texture

 If IO.File.Exists(content.File.FullName.Replace(content.File.Extension, ".map")) Then

 Try
 IO.File.Copy(content.File.FullName.Replace(content.File.Extension, ".map"), _
 Me.strExecutingFolder & content.File.FullName.Replace(Me.strProjectFolder, "").Replace(content.File.Extension, ".map"), True)
 Catch ex As Exception
 'I'm going to be optimistic and assume that if I can't copy
 'it's because the file is already there, and life is just fine
 End Try

 End If

 End If

 Next

 Me.strContentprojFile &= vbCrLf & vbTab & "<!-- To modify your build process, add your task inside one of the targets below and uncomment it. " & vbCrLf & _
 vbTab & "     Other similar extension points exist, see Microsoft.Common.targets." & vbCrLf & _
 vbTab & "<Target Name=""BeforeBuild"">" & vbCrLf & _
 vbTab & "</Target>" & vbCrLf & _
 vbTab & "<Target Name=""AfterBuild"">" & vbCrLf & _
 vbTab & "</Target>" & vbCrLf & _
 vbTab & "-->" & vbCrLf & _
 "</Project>"

 End Sub

End Class

0 comments: