using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.IO;
namespace OpenVIII
{
///
/// TEX file handler class. TEX files are packages of textures and Palettes.
///
///
/// I borrowed this from my Rinoa's toolset, but modified to aim for buffer rather than file-work
///
///
///
public class TEX : Texture_Base
{
#region Fields
///
/// Raw data of TEX file
///
private byte[] buffer;
private Texture texture;
#endregion Fields
#region Constructors
public TEX(byte[] buffer) => Load(buffer);
public TEX()
{
}
#endregion Constructors
/////
///// Contains header info and Palette data of TEX file.
/////
//public Texture TextureData => texture; //added to get texturedata outside of class.
#region Properties
public bool CLP => texture.PaletteFlag != 0;
public override byte GetBytesPerPixel => texture.bytesPerPixel;
public override int GetClutCount => texture.NumOfCluts;
public override int GetClutSize => (int)texture.PaletteSize;
public override int GetColorsCountPerPalette => texture.NumOfColours;
public override int GetHeight => texture.Height;
public override int GetOrigX => 0;
public override int GetOrigY => 0;
public override int GetWidth => texture.Width;
///
/// size of header section
///
private int Headersize => texture.Version <= 1 ? 0xEC : 0xF0;
///
/// size of palette section
///
private int PaletteSectionSize => (int)(texture.PaletteSize * 4);
///
/// start of texture section
///
private int TextureLocator => Headersize + PaletteSectionSize;
#endregion Properties
#region Methods
public override void ForceSetClutColors(ushort newNumOfColours) => texture.NumOfColours = newNumOfColours;
public override void ForceSetClutCount(ushort newClut) => texture.NumOfCluts = (byte)newClut;
public override Color[] GetClutColors(ushort clut)
{
if (!CLP)
return null;
else if (clut >= texture.NumOfCluts)
throw new Exception($"Desired palette is incorrect use -1 for default or use a smaller number: {clut} > {texture.NumOfCluts}");
Color[] colors = new Color[texture.NumOfColours];
int k = 0;
for (int i = clut * texture.NumOfColours * 4; i < texture.paletteData.Length && k < colors.Length; i += 4)
{
colors[k].B = texture.paletteData[i];
colors[k].G = texture.paletteData[i + 1];
colors[k].R = texture.paletteData[i + 2];
colors[k].A = texture.paletteData[i + 3];
k++;
}
return colors;
}
///
/// Get Texture2D converted to 32bit color
///
/// Desired Palette, see texture.NumOfPalettes. -1 is default.
/// Override colors of palette; Array size must match texture.NumOfColorsPerPalette
/// 32bit Texture2D
///
/// Some paletts are 256 but the game only uses 16 colors might need to make the restriction
/// more lax and allow any size array and only throw errors if the colorkey is greater than
/// size of array. Or we could treat any of those bad matches as transparent.
///
public override Texture2D GetTexture(Color[] colors = null)
{
if (Memory.graphics.GraphicsDevice != null)
{
if (texture.PaletteFlag != 0)
{
if (colors != null && colors.Length != texture.NumOfColours)
throw new Exception($" custom colors parameter set but array size to match palette size: {texture.NumOfColours}");
MemoryStream ms;
using (BinaryReader br = new BinaryReader(ms = new MemoryStream(buffer)))
{
ms.Seek(TextureLocator, SeekOrigin.Begin);
TextureBuffer convertBuffer = new TextureBuffer(texture.Width, texture.Height);
for (int i = 0; i < convertBuffer.Length && ms.Position < ms.Length; i++)
{
convertBuffer[i] = colors[br.ReadByte()]; //colorkey
}
return convertBuffer.GetTexture();
}
}
else if (texture.bytesPerPixel == 2)
{
MemoryStream ms;
using (BinaryReader br = new BinaryReader(ms = new MemoryStream(buffer)))
{
ms.Seek(TextureLocator, SeekOrigin.Begin);
TextureBuffer convertBuffer = new TextureBuffer(texture.Width, texture.Height);
for (int i = 0; ms.Position + 2 < ms.Length; i++)
{
convertBuffer[i] = ABGR1555toRGBA32bit(br.ReadUInt16());
}
return convertBuffer.GetTexture();
}
}
else if (texture.bytesPerPixel == 3)
{
// not tested but vincent tim had support for it so i guess it's possible RGB or BGR
MemoryStream ms;
using (BinaryReader br = new BinaryReader(ms = new MemoryStream(buffer)))
{
ms.Seek(TextureLocator, SeekOrigin.Begin);
TextureBuffer convertBuffer = new TextureBuffer(texture.Width, texture.Height);
Color color;
color.A = 0xFF;
for (int i = 0; ms.Position + 3 < ms.Length; i++)
{
//RGB or BGR so might need to reorder things to RGB
color.B = br.ReadByte();
color.G = br.ReadByte();
color.R = br.ReadByte();
convertBuffer[i] = color;
}
return convertBuffer.GetTexture();
}
}
}
return null;
}
public override Texture2D GetTexture(ushort? clut = null) => GetTexture(GetClutColors(clut ?? 0));
public override Texture2D GetTexture() => GetTexture(0);
public override void Load(byte[] buffer, uint offset = 0)
{
texture = new Texture();
this.buffer = buffer;
ReadParameters();
}
///
/// Writes the Tim file to the hard drive.
///
/// Path where you want file to be saved.
public override void Save(string path)
{
using (BinaryWriter bw = new BinaryWriter(File.Create(path)))
{
bw.Write(buffer);
}
}
public override void SaveCLUT(string path)
{
using (Texture2D CLUT = new Texture2D(Memory.graphics.GraphicsDevice, texture.NumOfColours, texture.NumOfCluts))
{
for (ushort i = 0; i < texture.NumOfCluts; i++)
{
CLUT.SetData(0, new Rectangle(0, i, texture.NumOfColours, 1), GetClutColors(i), 0, texture.NumOfColours);
}
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
CLUT.SaveAsPng(fs, texture.NumOfColours, texture.NumOfCluts);
}
}
///
/// Read header data from TEX file.
///
///
///
private void ReadParameters()
{
texture.Version = BitConverter.ToUInt32(buffer, 0x00);
texture.Width = (int)BitConverter.ToUInt32(buffer, 0x3C); //nothing will be uint size big.
texture.Height = (int)BitConverter.ToUInt32(buffer, 0x40);
texture.bytesPerPixel = buffer[0x68];
texture.NumOfCluts = buffer[0x30];
texture.NumOfColours = BitConverter.ToInt32(buffer, 0x34);
texture.bitDepth = BitConverter.ToUInt32(buffer, 0x38);
texture.PaletteFlag = buffer[0x4C];
texture.PaletteSize = BitConverter.ToUInt32(buffer, 0x58);
if (texture.PaletteFlag != 0)
{
texture.paletteData = new byte[PaletteSectionSize];
Buffer.BlockCopy(buffer, 0xF0, texture.paletteData, 0, PaletteSectionSize);
}
}
#endregion Methods
#region Structs
///
/// Contains Header info and Palette data of TEX file.
///
///
///
private struct Texture
{
#region Fields
///
/// 0x38
///
public uint bitDepth;
///
/// 0x68
///
public byte bytesPerPixel;
///
/// 0x40
///
public int Height;
///
/// 0x30
///
public byte NumOfCluts;
///
/// 0x34
///
public int NumOfColours;
///
/// 0xF0 for ff8;0xEC for ff7; size = PaletteSize * 4;
///
public byte[] paletteData;
///
/// 0x4C
///
public byte PaletteFlag;
///
/// 0x58
///
public uint PaletteSize;
///
/// 0x00; 1=FF7 | 2=FF8
///
public uint Version;
///
/// 0x3C
///
public int Width;
#endregion Fields
}
#endregion Structs
//public struct Color
//{
// #region Fields
// public byte Alpha; public byte Blue; public byte Green; public byte Red;
// #endregion Fields
//}
}
}