Xnb Exporter

Introduction

In the world of game development with Microsoft XNA, managing and exporting game assets like textures, fonts, models, and animations can be quite a task. To streamline this process, we’ve created a custom XNB exporter that simplifies exporting these assets into a more manageable format. In this post, we will explore the core components of this exporter and how it can be used to handle various types of content.

Key Features of the XNB Exporter

1. Initialization and Setup

public Exporter(string[] files, PictureBox pictureBox, string outPath = "")
{
    this.files = files;
    this.pictureBox = pictureBox;
    this.outPath = outPath;
    InitializeContentManager();
}

public void InitializeContentManager()
{
    var services = new GameServiceContainer();
    var graphicsDeviceService = GraphicsDeviceService.AddRef(IntPtr.Zero, 800, 600);
    services.AddService<IGraphicsDeviceService>(graphicsDeviceService);

    this.graphicsDevice = graphicsDeviceService.GraphicsDevice;
    string contentRootDirectory = Path.Combine(Application.StartupPath, "content");
    if (!Directory.Exists(contentRootDirectory))
    {
        Directory.CreateDirectory(contentRootDirectory);
    }
    contentManager = new ContentManager(services, contentRootDirectory);
}

The InitializeContentManager method sets up the ContentManager and GraphicsDevice, which are essential for handling XNA content.

2. Loading XNB Files

public List<XnbItem> Load()
{
    List<XnbItem> items = new List<XnbItem>();

    try
    {
        foreach (var file in files)
        {
            try
            {
                string relativePath = Path.GetRelativePath(ContentRootDirectory, file);

                string itemName = Path.ChangeExtension(relativePath, null);

                items.Add(new XnbItem { Name = itemName, Path = file });

                Debug.LogMessage($"Added item: {itemName}");
            }
            catch (ArgumentException ex)
            {
                Debug.LogException($"ArgumentException while processing file '{file}': {ex.Message}");
            }
            catch (Exception ex)
            {
                Debug.LogException($"Error processing file '{file}': {ex.Message}");
            }
        }

        OnCompleted?.Invoke();
    }
    catch (IOException ex)
    {
        Debug.LogException($"IOException during file loading: {ex.Message}");
    }
    catch (UnauthorizedAccessException ex)
    {
        Debug.LogException($"UnauthorizedAccessException during file loading: {ex.Message}");
    }
    catch (Exception ex)
    {
        Debug.LogException($"Unexpected error during file loading: {ex.Message}");
    }

    return items;
}

The Load method processes the given file paths, adds them to a list of XnbItem, and handles potential exceptions during the loading process.

3. Displaying Assets

Displaying Textures

public void DisplayImage(Texture2D texture)
{
    try
    {
        texture = ConvertToSupportedFormat(texture);
        Microsoft.Xna.Framework.Color[] textureData = new Microsoft.Xna.Framework.Color[texture.Width * texture.Height];
        texture.GetData(0, null, textureData, 0, texture.Width * texture.Height);

        using (var stream = new MemoryStream())
        {
            texture.SaveAsPng(stream, texture.Width, texture.Height);
            stream.Seek(0, SeekOrigin.Begin);
            pictureBox.Image = System.Drawing.Image.FromStream(stream);
        }
    }
    catch (Exception ex)
    {
        Debug.LogException(ex.Message);
    }
}

The DisplayImage method converts the texture to a supported format and displays it in a PictureBox.

Displaying Models

public void DisplaySkinnedModel(Model model, string modelName)
{
    if (graphicsDevice == null)
    {
        return;
    }
    try
    {
        graphicsDevice.BlendState = BlendState.Opaque;
        graphicsDevice.DepthStencilState = DepthStencilState.Default;

        foreach (ModelMesh mesh in model.Meshes)
        {
            foreach (ModelMeshPart part in mesh.MeshParts)
            {
                SharpDX.Direct3D9.Effect effect = null;
                mesh.Draw();
            }
        }
    }
    catch (Exception ex)
    {
        Debug.LogException("Failed to display skinned model '" + modelName + "': " + ex.Message);
    }
}

The DisplaySkinnedModel method is used to render and display a 3D model.

Displaying Fonts

public void DisplayFontAtlas(SpriteFont font, string fileName)
{
    using (var stream = new MemoryStream())
    {
        font.Texture.SaveAsPng(stream, font.Texture.Width, font.Texture.Height);
        stream.Seek(0, SeekOrigin.Begin);
        pictureBox.Image = System.Drawing.Image.FromStream(stream);
    }
}

The ApplyAnimation method applies animation transforms to the currently loaded model.

5. Converting Texture Formats

public Texture2D ConvertToSupportedFormat(Texture2D texture)
{
    if (IsDXTFormat(texture.Format))
    {
        var data = DecompressDXT(texture);
        var convertedTexture = new Texture2D(texture.GraphicsDevice, texture.Width, texture.Height, false, SurfaceFormat.Color);
        convertedTexture.SetData(data);
        return convertedTexture;
    }

    return texture;
}

The ConvertToSupportedFormat method ensures that textures are converted to a supported format if they are in a compressed format like DXT.

Conclusion

The custom XNB exporter provides a robust solution for managing and exporting XNA assets. With functionalities for loading, displaying, and converting various asset types, it streamlines the process of handling game content. Whether you’re working with textures, models, or animations, this exporter can be a valuable tool in your game development toolkit.

Feel free to customize and extend this exporter to better fit your needs. Happy exporting!