mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-03-19 00:21:20 +00:00
299 lines
10 KiB
C#
Executable file
299 lines
10 KiB
C#
Executable file
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using FSO.Files.Utils;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
using Microsoft.Xna.Framework;
|
|
using FSO.Common.Utils;
|
|
using FSO.Common;
|
|
using FSO.Common.Rendering;
|
|
|
|
namespace FSO.Files.Formats.IFF.Chunks
|
|
{
|
|
/// <summary>
|
|
/// This chunk type holds a number of paletted sprites that share a common color palette and lack z-buffers and
|
|
/// alpha buffers. SPR# chunks can be either big-endian or little-endian, which must be determined by comparing
|
|
/// the first two bytes to zero (since no version number uses more than two bytes).
|
|
/// </summary>
|
|
public class SPR : IffChunk
|
|
{
|
|
public List<SPRFrame> Frames { get; internal set; }
|
|
public ushort PaletteID;
|
|
private List<uint> Offsets;
|
|
public ByteOrder ByteOrd;
|
|
public bool WallStyle;
|
|
|
|
/// <summary>
|
|
/// Reads a SPR chunk from a stream.
|
|
/// </summary>
|
|
/// <param name="iff">An Iff instance.</param>
|
|
/// <param name="stream">A Stream object holding a SPR chunk.</param>
|
|
public override void Read(IffFile iff, Stream stream)
|
|
{
|
|
using (var io = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN))
|
|
{
|
|
var version1 = io.ReadUInt16();
|
|
var version2 = io.ReadUInt16();
|
|
uint version = 0;
|
|
|
|
if (version1 == 0)
|
|
{
|
|
io.ByteOrder = ByteOrder.BIG_ENDIAN;
|
|
version = (uint)(((version2|0xFF00)>>8) | ((version2&0xFF)<<8));
|
|
}
|
|
else
|
|
{
|
|
version = version1;
|
|
}
|
|
ByteOrd = io.ByteOrder;
|
|
|
|
var spriteCount = io.ReadUInt32();
|
|
PaletteID = (ushort)io.ReadUInt32();
|
|
|
|
Frames = new List<SPRFrame>();
|
|
if (version != 1001)
|
|
{
|
|
var offsetTable = new List<uint>();
|
|
for (var i = 0; i < spriteCount; i++)
|
|
{
|
|
offsetTable.Add(io.ReadUInt32());
|
|
}
|
|
Offsets = offsetTable;
|
|
for (var i = 0; i < spriteCount; i++)
|
|
{
|
|
var frame = new SPRFrame(this);
|
|
io.Seek(SeekOrigin.Begin, offsetTable[i]);
|
|
var guessedSize = ((i + 1 < offsetTable.Count) ? offsetTable[i + 1] : (uint)stream.Length) - offsetTable[i];
|
|
frame.Read(version, io, guessedSize);
|
|
Frames.Add(frame);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (io.HasMore)
|
|
{
|
|
var frame = new SPRFrame(this);
|
|
frame.Read(version, io, 0);
|
|
Frames.Add(frame);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The frame (I.E sprite) of a SPR chunk.
|
|
/// </summary>
|
|
public class SPRFrame : ITextureProvider
|
|
{
|
|
public static PALT DEFAULT_PALT = new PALT(Color.Black);
|
|
|
|
public uint Version;
|
|
private SPR Parent;
|
|
private Texture2D PixelCache;
|
|
private byte[] ToDecode;
|
|
|
|
/// <summary>
|
|
/// Constructs a new SPRFrame instance.
|
|
/// </summary>
|
|
/// <param name="parent">A SPR parent.</param>
|
|
public SPRFrame(SPR parent)
|
|
{
|
|
this.Parent = parent;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads a SPRFrame from a stream.
|
|
/// </summary>
|
|
/// <param name="iff">An Iff instance.</param>
|
|
/// <param name="stream">A Stream object holding a SPRFrame.</param>
|
|
public void Read(uint version, IoBuffer io, uint guessedSize)
|
|
{
|
|
if (version == 1001)
|
|
{
|
|
var spriteFersion = io.ReadUInt32();
|
|
|
|
var size = io.ReadUInt32();
|
|
this.Version = spriteFersion;
|
|
|
|
if (IffFile.RETAIN_CHUNK_DATA) ReadDeferred(1001, io);
|
|
else ToDecode = io.ReadBytes(size);
|
|
}
|
|
else
|
|
{
|
|
this.Version = version;
|
|
if (IffFile.RETAIN_CHUNK_DATA) ReadDeferred(1000, io);
|
|
else ToDecode = io.ReadBytes(guessedSize);
|
|
}
|
|
}
|
|
|
|
public void ReadDeferred(uint version, IoBuffer io)
|
|
{
|
|
var reserved = io.ReadUInt32();
|
|
var height = io.ReadUInt16();
|
|
var width = io.ReadUInt16();
|
|
this.Init(width, height);
|
|
this.Decode(io);
|
|
}
|
|
|
|
public void DecodeIfRequired()
|
|
{
|
|
if (ToDecode != null)
|
|
{
|
|
using (IoBuffer buf = IoBuffer.FromStream(new MemoryStream(ToDecode), Parent.ByteOrd))
|
|
{
|
|
ReadDeferred(Version, buf);
|
|
}
|
|
|
|
ToDecode = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decodes this SPRFrame.
|
|
/// </summary>
|
|
/// <param name="io">IOBuffer used to read a SPRFrame.</param>
|
|
private void Decode(IoBuffer io)
|
|
{
|
|
var palette = Parent.ChunkParent.Get<PALT>(Parent.PaletteID);
|
|
if (palette == null)
|
|
{
|
|
palette = DEFAULT_PALT;
|
|
}
|
|
|
|
var y = 0;
|
|
var endmarker = false;
|
|
|
|
while (!endmarker){
|
|
var command = io.ReadByte();
|
|
var count = io.ReadByte();
|
|
|
|
switch (command){
|
|
/** Start marker **/
|
|
case 0x00:
|
|
case 0x10:
|
|
break;
|
|
/** Fill row with pixel data **/
|
|
case 0x04:
|
|
var bytes = count - 2;
|
|
var x = 0;
|
|
|
|
while (bytes > 0){
|
|
var pxCommand = io.ReadByte();
|
|
var pxCount = io.ReadByte();
|
|
bytes -= 2;
|
|
|
|
switch (pxCommand){
|
|
/** Next {n} pixels are transparent **/
|
|
case 0x01:
|
|
x += pxCount;
|
|
break;
|
|
/** Next {n} pixels are the same palette color **/
|
|
case 0x02:
|
|
var index = io.ReadByte();
|
|
var padding = io.ReadByte();
|
|
bytes -= 2;
|
|
|
|
var color = palette.Colors[index];
|
|
for (var j=0; j < pxCount; j++){
|
|
this.SetPixel(x, y, color);
|
|
x++;
|
|
}
|
|
break;
|
|
/** Next {n} pixels are specific palette colours **/
|
|
case 0x03:
|
|
for (var j=0; j < pxCount; j++){
|
|
var index2 = io.ReadByte();
|
|
var color2 = palette.Colors[index2];
|
|
this.SetPixel(x, y, color2);
|
|
x++;
|
|
}
|
|
bytes -= pxCount;
|
|
if (pxCount % 2 != 0){
|
|
//Padding
|
|
io.ReadByte();
|
|
bytes--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
y++;
|
|
break;
|
|
/** End marker **/
|
|
case 0x05:
|
|
endmarker = true;
|
|
break;
|
|
/** Leave next rows transparent **/
|
|
case 0x09:
|
|
y += count;
|
|
continue;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private Color[] Data;
|
|
public int Width { get; internal set; }
|
|
public int Height { get; internal set; }
|
|
|
|
protected void Init(int width, int height)
|
|
{
|
|
this.Width = width;
|
|
this.Height = height;
|
|
Data = new Color[Width * Height];
|
|
}
|
|
|
|
public Color GetPixel(int x, int y)
|
|
{
|
|
return Data[(y * Width) + x];
|
|
}
|
|
|
|
public void SetPixel(int x, int y, Color color)
|
|
{
|
|
Data[(y * Width) + x] = color;
|
|
}
|
|
|
|
public Texture2D GetTexture(GraphicsDevice device)
|
|
{
|
|
DecodeIfRequired();
|
|
if (PixelCache == null)
|
|
{
|
|
var mip = !Parent.WallStyle && FSOEnvironment.Enable3D && FSOEnvironment.EnableNPOTMip;
|
|
var tc = FSOEnvironment.TexCompress;
|
|
|
|
if (Width * Height > 0)
|
|
{
|
|
var w = Math.Max(1, Width);
|
|
var h = Math.Max(1, Height);
|
|
if (mip && TextureUtils.OverrideCompression(w, h)) tc = false;
|
|
if (tc)
|
|
{
|
|
PixelCache = new Texture2D(device, ((w+3)/4)*4, ((h+3)/4)*4, mip, SurfaceFormat.Dxt5);
|
|
if (mip)
|
|
TextureUtils.UploadDXT5WithMips(PixelCache, w, h, device, Data);
|
|
else
|
|
PixelCache.SetData<byte>(TextureUtils.DXT5Compress(Data, w, h).Item1);
|
|
}
|
|
else
|
|
{
|
|
PixelCache = new Texture2D(device, w, h, mip, SurfaceFormat.Color);
|
|
if (mip)
|
|
TextureUtils.UploadWithMips(PixelCache, device, Data);
|
|
else
|
|
PixelCache.SetData<Color>(this.Data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PixelCache = new Texture2D(device, Math.Max(1, Width), Math.Max(1, Height), mip, SurfaceFormat.Color);
|
|
PixelCache.SetData<Color>(new Color[] { Color.Transparent });
|
|
}
|
|
|
|
PixelCache.Tag = new TextureInfo(PixelCache, Width, Height);
|
|
if (!IffFile.RETAIN_CHUNK_DATA) Data = null;
|
|
}
|
|
return PixelCache;
|
|
}
|
|
}
|
|
}
|