using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Diagnostics.CodeAnalysis;
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 sealed 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 texture data 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.NumOfColors;
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 newNumOfColors) => _texture.NumOfColors = newNumOfColors;
public override void ForceSetClutCount(ushort newClut) => _texture.NumOfCluts = (byte)newClut;
public override Color[] GetClutColors(ushort clut)
{
if (!CLP || _texture.NumOfCluts == 0)
return null;
if (clut >= _texture.NumOfCluts)
throw new Exception($"Desired palette is incorrect use -1 for default or use a smaller number: {clut} > {_texture.NumOfCluts}");
var colors = new Color[_texture.NumOfColors];
var k = 0;
for (var i = clut * _texture.NumOfColors * 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 palettes 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 color key is greater than
/// size of array. Or we could treat any of those bad matches as transparent.
///
public override Texture2D GetTexture(Color[] colors)
{
if (Memory.Graphics.GraphicsDevice == null) return null;
if (_texture.PaletteFlag != 0)
{
if (colors == null) throw new ArgumentNullException(nameof(colors));
//if (colors != null && colors.Length != texture.NumOfColors)
//{
// //if (colors.Length > texture.NumOfColors) //truncate colors to the correct amount. in some
// // colors = colors.Take(texture.NumOfColors).ToArray();
// //else // might need to expand the array the other way if we get more mismatches.
// //Array.Resize(ref colors,texture.NumOfColors);
// //throw new Exception($" custom colors parameter set but array size to match palette size: {texture.NumOfColors}");
//}
MemoryStream ms;
using (var br = new BinaryReader(ms = new MemoryStream(_buffer)))
{
ms.Seek(TextureLocator, SeekOrigin.Begin);
var convertBuffer = new TextureBuffer(_texture.Width, _texture.Height);
for (var i = 0; i < convertBuffer.Length && ms.Position < ms.Length; i++)
{
var colorKey = br.ReadByte();
if (colorKey > colors.Length) continue;
convertBuffer[i] = colors[colorKey];
}
return convertBuffer.GetTexture();
}
}
if (_texture.BytesPerPixel == 2)
{
MemoryStream ms;
using (var br = new BinaryReader(ms = new MemoryStream(_buffer)))
{
ms.Seek(TextureLocator, SeekOrigin.Begin);
var convertBuffer = new TextureBuffer(_texture.Width, _texture.Height);
for (var i = 0; ms.Position + 2 < ms.Length; i++)
{
convertBuffer[i] = ABGR1555toRGBA32bit(br.ReadUInt16());
}
return convertBuffer.GetTexture();
}
}
if (_texture.BytesPerPixel != 3) return null;
{
// not tested but vincent tim had support for it so i guess it's possible RGB or BGR
MemoryStream ms;
using (var br = new BinaryReader(ms = new MemoryStream(_buffer)))
{
ms.Seek(TextureLocator, SeekOrigin.Begin);
var convertBuffer = new TextureBuffer(_texture.Width, _texture.Height);
var color = new Color { A = 0xFF };
for (var 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();
}
}
}
public override Texture2D GetTexture(ushort clut) => GetTexture(GetClutColors(clut));
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 (var bw = new BinaryWriter(File.Create(path)))
{
bw.Write(_buffer);
}
}
public override void SaveCLUT(string path)
{
using (var clut = new Texture2D(Memory.Graphics.GraphicsDevice, _texture.NumOfColors, _texture.NumOfCluts))
{
for (ushort i = 0; i < _texture.NumOfCluts; i++)
{
clut.SetData(0, new Rectangle(0, i, _texture.NumOfColors, 1), GetClutColors(i), 0, _texture.NumOfColors);
}
Extended.Save_As_PNG(clut, path, _texture.NumOfColors, _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.NumOfColors = BitConverter.ToInt32(_buffer, 0x34);
_texture.BitDepth = BitConverter.ToUInt32(_buffer, 0x38);
_texture.PaletteFlag = _buffer[0x4C];
_texture.PaletteSize = BitConverter.ToUInt32(_buffer, 0x58);
if (_texture.PaletteFlag == 0) return;
_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
///
[SuppressMessage("ReSharper", "NotAccessedField.Local")] public uint BitDepth;
///
/// 0x68
///
public byte BytesPerPixel;
///
/// 0x40
///
public int Height;
///
/// 0x30
///
public byte NumOfCluts;
///
/// 0x34
///
public int NumOfColors;
///
/// 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
//}
}
}