GARbro-mirror/ArcFormats/Entis/ImageERI.cs

230 lines
8.7 KiB
C#

//! \file ImageERI.cs
//! \date Tue May 26 12:04:30 2015
//! \brief Entis rasterized image format.
//
// Copyright (C) 2015 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
using GameRes.Utility;
namespace GameRes.Formats.Entis
{
internal class EriMetaData : ImageMetaData
{
public int StreamPos;
public int Version;
public CvType Transformation;
public EriCode Architecture;
public int FormatType;
public bool VerticalFlip;
public int ClippedPixel;
public int SamplingFlags;
public ulong QuantumizedBits;
public ulong AllottedBits;
public int BlockingDegree;
public int LappedBlock;
public int FrameTransform;
public int FrameDegree;
}
public enum CvType
{
Lossless_ERI = 0x03020000,
DCT_ERI = 0x00000001,
LOT_ERI = 0x00000005,
LOT_ERI_MSS = 0x00000105,
}
public enum EriCode
{
RunlengthGamma = -1,
RunlengthHuffman = -4,
Nemesis = -16,
}
public enum EriImage
{
RGB = 0x00000001,
RGBA = 0x04000001,
Gray = 0x00000002,
TypeMask = 0x00FFFFFF,
WithPalette = 0x01000000,
UseClipping = 0x02000000,
WithAlpha = 0x04000000,
SideBySide = 0x10000000,
}
internal class EriFile : BinaryReader
{
internal struct Section
{
public AsciiString Id;
public long Length;
}
public EriFile (Stream stream) : base (stream, System.Text.Encoding.ASCII, true)
{
}
public Section ReadSection ()
{
var section = new Section();
section.Id = new AsciiString (8);
if (8 != this.Read (section.Id.Value, 0, 8))
throw new EndOfStreamException();
section.Length = this.ReadInt64();
return section;
}
public long FindSection (string name)
{
var id = new AsciiString (8);
for (;;)
{
if (8 != this.Read (id.Value, 0, 8))
throw new EndOfStreamException();
var length = this.ReadInt64();
if (length < 0)
throw new EndOfStreamException();
if (id == name)
return length;
this.BaseStream.Seek (length, SeekOrigin.Current);
}
}
}
[Export(typeof(ImageFormat))]
public class EriFormat : ImageFormat
{
public override string Tag { get { return "ERI"; } }
public override string Description { get { return "Entis rasterized image format"; } }
public override uint Signature { get { return 0x69746e45u; } } // 'Enti'
public override ImageMetaData ReadMetaData (Stream stream)
{
byte[] header = new byte[0x40];
if (header.Length != stream.Read (header, 0, header.Length))
return null;
if (0x03000100 != LittleEndian.ToUInt32 (header, 8))
return null;
if (!Binary.AsciiEqual (header, 0x10, "Entis Rasterized Image"))
return null;
using (var reader = new EriFile (stream))
{
var section = reader.ReadSection();
if (section.Id != "Header " || section.Length <= 0)
return null;
int header_size = (int)section.Length;
int stream_pos = 0x50 + header_size;
EriMetaData info = null;
while (header_size > 8)
{
section = reader.ReadSection();
header_size -= 8;
if (section.Length <= 0 || section.Length > header_size)
break;
if ("ImageInf" == section.Id)
{
int version = reader.ReadInt32();
if (version != 0x00020100 && version != 0x00020200)
return null;
info = new EriMetaData { StreamPos = stream_pos, Version = version };
info.Transformation = (CvType)reader.ReadInt32();
info.Architecture = (EriCode)reader.ReadInt32();
info.FormatType = reader.ReadInt32();
int w = reader.ReadInt32();
int h = reader.ReadInt32();
info.Width = (uint)Math.Abs (w);
info.Height = (uint)Math.Abs (h);
info.VerticalFlip = h < 0;
info.BPP = reader.ReadInt32();
info.ClippedPixel = reader.ReadInt32();
info.SamplingFlags = reader.ReadInt32();
info.QuantumizedBits = reader.ReadUInt64();
info.AllottedBits = reader.ReadUInt64();
info.BlockingDegree = reader.ReadInt32();
info.LappedBlock = reader.ReadInt32();
info.FrameTransform = reader.ReadInt32();
info.FrameDegree = reader.ReadInt32();
break;
}
header_size -= (int)section.Length;
reader.BaseStream.Seek (section.Length, SeekOrigin.Current);
}
return info;
}
}
public override ImageData Read (Stream stream, ImageMetaData info)
{
var meta = info as EriMetaData;
if (null == meta)
throw new ArgumentException ("EriFormat.Read should be supplied with EriMetaData", "info");
stream.Position = meta.StreamPos;
using (var input = new EriFile (stream))
{
Color[] palette = null;
for (;;) // ReadSection throws an exception in case of EOF
{
var section = input.ReadSection();
if ("Stream " == section.Id)
continue;
if ("ImageFrm" == section.Id)
break;
if ("Palette " == section.Id && info.BPP <= 8 && section.Length <= 0x400)
{
palette = ReadPalette (stream, (int)section.Length);
continue;
}
input.BaseStream.Seek (section.Length, SeekOrigin.Current);
}
var reader = new EriReader (stream, meta, palette);
reader.DecodeImage();
return ImageData.Create (info, reader.Format, reader.Palette, reader.Data, reader.Stride);
}
}
private Color[] ReadPalette (Stream input, int palette_length)
{
var palette_data = new byte[0x400];
if (palette_length > palette_data.Length)
throw new InvalidFormatException();
if (palette_length != input.Read (palette_data, 0, palette_length))
throw new InvalidFormatException();
var colors = new Color[256];
for (int i = 0; i < 256; ++i)
{
colors[i] = Color.FromRgb (palette_data[i*4+2], palette_data[i*4+1], palette_data[i*4]);
}
return colors;
}
public override void Write (Stream file, ImageData image)
{
throw new NotImplementedException ("EriFormat.Write not implemented");
}
}
}