summaryrefslogtreecommitdiff
path: root/WADEntryConvert.cs
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2005-01-21 00:52:26 +0000
committerIan C <ianc@noddybox.co.uk>2005-01-21 00:52:26 +0000
commit5bb3e70344cfafa8c09561b6d7d157026ce18b4f (patch)
tree072d4c6764c67dee203e7d8d5294975e96c41c39 /WADEntryConvert.cs
parent90648a22d6268df497cd4384e6ba7a0ae4e23b8b (diff)
Initial checkin
Diffstat (limited to 'WADEntryConvert.cs')
-rw-r--r--WADEntryConvert.cs685
1 files changed, 685 insertions, 0 deletions
diff --git a/WADEntryConvert.cs b/WADEntryConvert.cs
new file mode 100644
index 0000000..7cd52b3
--- /dev/null
+++ b/WADEntryConvert.cs
@@ -0,0 +1,685 @@
+// Noddybox.DOOM - DOOM WAD Wrapper
+// Copyright (C) 2004 Ian Cowburn
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+using System;
+using System.Collections;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Resources;
+using System.Reflection;
+using System.Text;
+
+namespace Noddybox.DOOM
+{
+ /// <summary>
+ /// Provides routines to convert WADEntrys to other entities
+ /// </summary>
+ public class WADEntryConvert
+ {
+ // ====================================
+ // PUBLIC
+ // ====================================
+
+ /// <summary>
+ /// Converts a palette lump into an array of 256 colours.
+ /// </summary>
+ /// <param name="e">The palette lump, or null for the default DOOM 2
+ /// palette</param>
+ /// <returns>The colours</returns>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// Lump is too small to hold a palette
+ /// </exception>
+ public static Color[] Palette(WADEntry e)
+ {
+ if (e==null)
+ {
+ ResourceManager mgr=new ResourceManager("DOOM",
+ Assembly.GetExecutingAssembly());
+
+ Bitmap bmp=(Bitmap)mgr.GetObject("palette");
+
+ if (bmp==null)
+ {
+ throw new DoomException("Failed to retrieve DOOM 2 palette from resources");
+ }
+
+ return bmp.Palette.Entries;
+ }
+
+ if (e.Length<768)
+ {
+ throw new DoomException("Not enough in LUMP " + e.Name + " for a palette");
+ }
+
+ Color[] pal=new Color[256];;
+
+ for(int f=0;f<256;f++)
+ {
+ int r=e.Data[f*3+0];
+ int g=e.Data[f*3+1];
+ int b=e.Data[f*3+2];
+
+ pal[f]=Color.FromArgb(r,g,b);
+ }
+
+ return pal;
+ }
+
+ /// <summary>
+ /// Converts a Doom texture/sprite lump into a bitmap
+ /// </summary>
+ /// <param name="e">The lump</param>
+ /// <param name="pal">The palette</param>
+ /// <returns>The 8-bit bitmap</returns>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// There aren't 256 colours in the palette
+ /// </exception>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// Empty lump
+ /// </exception>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// Nonsense size
+ /// </exception>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// Short lump
+ /// </exception>
+ public static Bitmap Gfx(WADEntry e, Color[] pal)
+ {
+ if (pal.Length<256)
+ {
+ throw new DoomException("Need a 256 colour palette to create images");
+ }
+
+ if (e.Length==0)
+ {
+ throw new DoomException("Empty lump");
+ }
+
+ MemoryStream str=e.Stream;
+
+ int lw=GetWord(str);
+ int lh=GetWord(str);
+
+ if (lw<1 || lw>1024 || lh<1 || lh>1024)
+ {
+ throw new DoomException("Nonsense size");
+ }
+
+ // Ignore the offsets
+ //
+ GetWord(str);
+ GetWord(str);
+
+ Bitmap bmp=new Bitmap(lw,lh,PixelFormat.Format8bppIndexed);
+
+ SetBitmapPal(bmp,pal);
+
+ BitmapData bmp_data=bmp.LockBits(new Rectangle(0,0,bmp.Width,bmp.Height),
+ ImageLockMode.WriteOnly,
+ PixelFormat.Format8bppIndexed);
+ try
+ {
+ for(int x=0;x<lw;x++)
+ {
+ uint offset=GetLong(str);
+
+ try
+ {
+ while(e[offset]!=0xffu)
+ {
+ int y=e[offset++];
+ int cno=e[offset++];
+
+ offset++;
+
+ for(int f=0;f<cno;f++)
+ {
+ if (x>=0 && x<lw && y>=0 && y<lh)
+ {
+ Write8bppPixel(bmp_data,x,y,e[offset]);
+ }
+
+ offset++;
+ y++;
+ }
+
+ offset++;
+ }
+ }
+ catch (IndexOutOfRangeException)
+ {
+ throw new DoomException("Short lump");
+ }
+ }
+ }
+ finally
+ {
+ bmp.UnlockBits(bmp_data);
+ }
+
+ return bmp;
+ }
+
+ /// <summary>
+ /// Converts a flat-style lump into a bitmap
+ /// </summary>
+ /// <param name="e">The lump</param>
+ /// <param name="pal">The palette</param>
+ /// <returns>The 8-bit bitmap</returns>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// There aren't 256 colours in the palette
+ /// </exception>
+ public static Bitmap Flat(WADEntry e, Color[] pal)
+ {
+ if (pal.Length<256)
+ {
+ throw new DoomException("Need a 256 colour palette to create images");
+ }
+
+ if (e.Length!=4096 && e.Length!=16384 && e.Length!=65536)
+ {
+ throw new DoomException("FLAT Lump must be 4096, 16384 or 65536 bytes");
+ }
+
+ int size=(int)Math.Sqrt(e.Length);
+
+ Bitmap bmp=new Bitmap(size,size,PixelFormat.Format8bppIndexed);
+
+ SetBitmapPal(bmp,pal);
+
+ for(int f=0;f<256;f++)
+ {
+ bmp.Palette.Entries[f]=pal[f];
+ }
+
+ BitmapData bmp_data=bmp.LockBits(new Rectangle(0,0,bmp.Width,bmp.Height),
+ ImageLockMode.WriteOnly,
+ PixelFormat.Format8bppIndexed);
+ try
+ {
+ int i=0;
+
+ for(int y=0;y<size;y++)
+ {
+ for(int x=0;x<size;x++)
+ {
+ Write8bppPixel(bmp_data,x,y,e[i++]);
+ }
+ }
+ }
+ finally
+ {
+ bmp.UnlockBits(bmp_data);
+ }
+
+ return bmp;
+ }
+
+ /// <summary>
+ /// Convert a bitmap into a graphical lump
+ /// </summary>
+ /// <param name="name">The name to give the new lump</param>
+ /// <param name="bmp">The 8-bit indexed bitmap</param>
+ /// <param name="xoff">The X-offset to set in the graphic lump</param>
+ /// <param name="yoff">The Y-offset to set in the graphic lump</param>
+ /// <param name="trans">If true, assume colour 247 is transparent</param>
+ /// <returns>The lump</returns>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// The bitmap isn't 8bpp, indexed
+ /// </exception>
+ public static WADEntry Gfx(string name, Bitmap bmp, int xoff, int yoff, bool trans)
+ {
+ if (bmp.PixelFormat!=PixelFormat.Format8bppIndexed)
+ {
+ throw new DoomException("Bitmap must be 8-bits, indexed");
+ }
+
+ if (bmp.Height>255)
+ {
+ throw new DoomException("Doom graphics cannot be over 255 pixels high");
+ }
+
+ ArrayList al=new ArrayList();
+ MemoryStream str=new MemoryStream();
+
+ uint data_offset=8u+4u*(uint)bmp.Width;
+
+ PutWord(str,bmp.Width);
+ PutWord(str,bmp.Height);
+ PutWord(str,xoff);
+ PutWord(str,yoff);
+
+ BitmapData bmp_data=bmp.LockBits(new Rectangle(0,0,bmp.Width,bmp.Height),
+ ImageLockMode.ReadOnly,
+ PixelFormat.Format8bppIndexed);
+ try
+ {
+ for(int x=0;x<bmp.Width;x++)
+ {
+ PutLong(str,data_offset+(uint)al.Count);
+
+ if (trans)
+ {
+ EncodeTransColumn(bmp_data,x,bmp.Height,al);
+ }
+ else
+ {
+ EncodeNonTransColumn(bmp_data,x,bmp.Height,al);
+ }
+ }
+ }
+ finally
+ {
+ bmp.UnlockBits(bmp_data);
+ }
+
+ str.Write((byte[])al.ToArray(typeof(byte)),0,al.Count);
+ str.Close();
+
+ return new WADEntry(name,str.ToArray());
+ }
+
+ /// <summary>
+ /// Convert a bitmap into a graphical lump
+ /// </summary>
+ /// <param name="name">The name to give the new lump</param>
+ /// <param name="bmp">The 8-bit indexed bitmap</param>
+ /// <param name="trans">If true, assume colour 247 is transparent</param>
+ /// <returns>The lump</returns>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// The bitmap isn't 8bpp, indexed
+ /// </exception>
+ public static WADEntry Gfx(string name, Bitmap bmp, bool trans)
+ {
+ return Gfx(name,bmp,0,0,trans);
+ }
+
+ /// <summary>
+ /// Convert a flat into a lump
+ /// </summary>
+ /// <param name="name">The name to give the new lump</param>
+ /// <param name="bmp">The 8-bit indexed bitmap</param>
+ /// <returns>The lump</returns>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// The bitmap isn't 8bpp, indexed
+ /// </exception>
+ public static WADEntry Flat(string name, Bitmap bmp)
+ {
+ if (bmp.Width!=bmp.Height ||(bmp.Width!=64 &&
+ bmp.Width!=128 &&
+ bmp.Width!=256))
+ {
+ throw new DoomException("Flats must be 64x64, 128x128 or 256x256");
+ }
+
+ if (bmp.PixelFormat!=PixelFormat.Format8bppIndexed)
+ {
+ throw new DoomException("Bitmap must be 8-bits, indexed");
+ }
+
+ byte[] data=new byte[bmp.Width*bmp.Height];
+ BitmapData bmp_data=bmp.LockBits(new Rectangle(0,0,bmp.Width,bmp.Height),
+ ImageLockMode.ReadOnly,
+ PixelFormat.Format8bppIndexed);
+ try
+ {
+ for(int y=0;y<bmp.Height;y++)
+ {
+ for(int x=0;x<bmp.Width;x++)
+ {
+ data[x+y*bmp.Width]=Read8bppPixel(bmp_data,x,y);
+ }
+ }
+ }
+ finally
+ {
+ bmp.UnlockBits(bmp_data);
+ }
+
+ return new WADEntry(name,data);
+ }
+
+ /// <summary>
+ /// Converts a sample lump to a WAV file
+ /// </summary>
+ /// <param name="file">The stream to write the WAV into. Closed on exit.</param>
+ /// <param name="e">The lump containing the sample data</param>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// Not a sample lump
+ /// </exception>
+ public static void Wav(Stream file, WADEntry e)
+ {
+ MemoryStream str=e.Stream;
+
+ try
+ {
+ if (GetWord(str)!=3)
+ {
+ throw new DoomException("Not a sample lump");
+ }
+
+ int rate=GetWord(str);
+ int len=GetWord(str);
+
+ if (GetWord(str)!=0 || len>e.Length-8)
+ {
+ throw new DoomException("Not a sample lump");
+ }
+
+ // Write RIFF header
+ //
+ file.Write(m_ascii.GetBytes("RIFF"),0,4);
+ PutLong(file,(uint)len+36u);
+ file.Write(m_ascii.GetBytes("WAVE"),0,4);
+
+ // Write 'fmt' chunk
+ //
+ file.Write(m_ascii.GetBytes("fmt "),0,4);
+ PutLong(file,16); // Size
+ PutWord(file,1); // PCM
+ PutWord(file,1); // No channels
+ PutLong(file,(uint)rate); // Sample Rate
+ PutLong(file,(uint)rate); // Byte rate
+ PutWord(file,1); // Block align
+ PutWord(file,8); // Bits per sample
+
+ // Write 'data' chunk
+ //
+ file.Write(m_ascii.GetBytes("data"),0,4);
+ PutLong(file,(uint)len);
+
+ while(str.Position<str.Length)
+ {
+ file.WriteByte((byte)str.ReadByte());
+ }
+ }
+ finally
+ {
+ file.Close();
+ str.Close();
+ }
+ }
+
+ /// <summary>
+ /// Converts a WAV file to a sample lump
+ /// </summary>
+ /// <param name="file">The stream containing the WAV. Closed on exit.</param>
+ /// <returns>The lump</returns>
+ /// <exception cref="Noddybox.DOOM.DoomException">
+ /// Must be a WAV, uncompressed, mono, 8-bits and less than 65535 samples
+ /// </exception>
+ public static WADEntry Wav(string name, Stream file)
+ {
+ string error="Must be a WAV, uncompressed, mono, 8-bits and less than 65535 samples";
+ WADEntry e=new WADEntry();
+ MemoryStream data=new MemoryStream();
+
+ e.Name=name;
+
+ try
+ {
+ // Get RIFF Header
+ //
+ if (Get4CharString(file)!="RIFF")
+ {
+ throw new DoomException(error);
+ }
+
+ GetLong(file);
+
+ if (Get4CharString(file)!="WAVE")
+ {
+ throw new DoomException(error);
+ }
+
+ // Find 'fmt' chunk
+ //
+ while(Get4CharString(file)!="fmt ")
+ {
+ uint chunklen=GetLong(file);
+ file.Position+=chunklen;
+ }
+
+ GetLong(file);
+
+ int pcm=GetWord(file);
+ int chan=GetWord(file);
+ uint rate=GetLong(file);
+ GetLong(file);
+ int align=GetWord(file);
+ int bps=GetWord(file);
+
+ if (pcm!=1 || chan!=1 || align!=1 || bps!=8)
+ {
+ throw new DoomException(error);
+ }
+
+ PutWord(data,3);
+ PutWord(data,(int)rate);
+
+ // Find 'data' chunk
+ //
+ while(Get4CharString(file)!="data")
+ {
+ uint chunklen=GetLong(file);
+ file.Position+=chunklen;
+ }
+
+ uint len=GetLong(file);
+
+ if (len>65535)
+ {
+ throw new DoomException(error);
+ }
+
+ PutWord(data,(int)len);
+ PutWord(data,0);
+
+ byte[] buff=new byte[len];
+
+ file.Read(buff,0,(int)len);
+ data.Write(buff,0,(int)len);
+
+ e.Data=data.ToArray();
+ }
+ finally
+ {
+ data.Close();
+ file.Close();
+ }
+
+ return e;
+ }
+
+ // ====================================
+ // PRIVATE
+ // ====================================
+
+ private static ASCIIEncoding m_ascii=new ASCIIEncoding();
+
+ private WADEntryConvert() {}
+
+ private static void SetBitmapPal(Bitmap bmp, Color[] pal)
+ {
+ ColorPalette bmppal=bmp.Palette;
+
+ for(int f=0;f<256;f++)
+ {
+ bmppal.Entries[f]=pal[f];
+ }
+
+ bmp.Palette=bmppal;
+ }
+
+ private static byte Read8bppPixel(BitmapData data, int x, int y)
+ {
+ unsafe
+ {
+ byte *p=(byte *)data.Scan0+x+y*data.Stride;
+ return *p;
+ }
+ }
+
+ private static void Write8bppPixel(BitmapData data, int x, int y, byte col)
+ {
+ unsafe
+ {
+ byte *p=(byte *)data.Scan0+x+y*data.Stride;
+ *p=col;
+ }
+ }
+
+ private static int GetWord(Stream str)
+ {
+ int w=0;
+
+ for(int f=0;f<2;f++)
+ {
+ if (str.Position>str.Length)
+ {
+ throw new DoomException("Unexpected EOF");
+ }
+
+ w|=str.ReadByte()<<(f*8);
+ }
+
+ return w;
+ }
+
+ private static uint GetLong(Stream str)
+ {
+ uint l=0;
+
+ for(int f=0;f<4;f++)
+ {
+ if (str.Position>str.Length)
+ {
+ throw new DoomException("Unexpected EOF");
+ }
+
+ l|=(uint)str.ReadByte()<<(f*8);
+ }
+
+ return l;
+ }
+
+ private static string Get4CharString(Stream str)
+ {
+ byte[] b=new byte[4];
+
+ for(int f=0;f<4;f++)
+ {
+ if (str.Position>str.Length)
+ {
+ throw new DoomException("Unexpected EOF");
+ }
+
+ b[f]=(byte)str.ReadByte();
+ }
+
+ return m_ascii.GetString(b);
+ }
+
+ private static void PutWord(Stream str, int w)
+ {
+ for(int f=0;f<2;f++)
+ {
+ str.WriteByte((byte)(w&0xff));
+ w>>=8;
+ }
+ }
+
+ private static void PutLong(Stream str, uint w)
+ {
+ for(int f=0;f<4;f++)
+ {
+ str.WriteByte((byte)(w&0xff));
+ w>>=8;
+ }
+ }
+
+ private static void EncodeTransColumn(BitmapData bmp,
+ int x,
+ int height,
+ ArrayList al)
+ {
+ ArrayList block=null;
+ byte block_start=0;
+
+ for(byte y=0;y<height;y++)
+ {
+ byte col=Read8bppPixel(bmp,x,y);
+
+ if (col!=247)
+ {
+ if (block==null)
+ {
+ block=new ArrayList();
+ block_start=y;
+ }
+
+ block.Add(col);
+ }
+ else
+ {
+ if (block!=null)
+ {
+ al.Add(block_start);
+ al.Add((byte)block.Count);
+ al.Add((byte)0);
+ al.AddRange(block);
+ al.Add((byte)0);
+ block=null;
+ }
+ }
+ }
+
+ if (block!=null)
+ {
+ al.Add(block_start);
+ al.Add((byte)block.Count);
+ al.Add((byte)0);
+ al.AddRange(block);
+ al.Add((byte)0);
+ block=null;
+ }
+
+ al.Add((byte)255);
+ }
+
+ private static void EncodeNonTransColumn(BitmapData bmp,
+ int x,
+ int height,
+ ArrayList al)
+ {
+ al.Add((byte)0);
+ al.Add((byte)height);
+
+ al.Add((byte)0);
+
+ for(byte y=0;y<height;y++)
+ {
+ al.Add(Read8bppPixel(bmp,x,y));
+ }
+
+ al.Add((byte)0);
+
+ al.Add((byte)255);
+ }
+ }
+}