// 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. // // $Id$ // 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 { /// /// Provides routines to convert WADEntrys to other entities /// public class WADEntryConvert { // ==================================== // PUBLIC // ==================================== /// /// Converts a palette lump into an array of 256 colours. /// /// The palette lump, or null for the default DOOM 2 /// palette /// The colours /// /// Lump is too small to hold a palette /// 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; } /// /// Converts a Doom texture/sprite lump into a bitmap /// /// The lump /// The palette /// The 8-bit bitmap /// /// There aren't 256 colours in the palette /// /// /// Empty lump /// /// /// Nonsense size /// /// /// Short lump /// 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=0 && x=0 && y /// Converts a flat-style lump into a bitmap /// /// The lump /// The palette /// The 8-bit bitmap /// /// There aren't 256 colours in the palette /// 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 /// Convert a bitmap into a graphical lump /// /// The name to give the new lump /// The 8-bit indexed bitmap /// The X-offset to set in the graphic lump /// The Y-offset to set in the graphic lump /// If true, assume colour 247 is transparent /// The lump /// /// The bitmap isn't 8bpp, indexed /// 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 /// Convert a bitmap into a graphical lump /// /// The name to give the new lump /// The 8-bit indexed bitmap /// If true, assume colour 247 is transparent /// The lump /// /// The bitmap isn't 8bpp, indexed /// public static WADEntry Gfx(string name, Bitmap bmp, bool trans) { return Gfx(name,bmp,0,0,trans); } /// /// Convert a flat into a lump /// /// The name to give the new lump /// The 8-bit indexed bitmap /// The lump /// /// The bitmap isn't 8bpp, indexed /// 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 /// Converts a sample lump to a WAV file /// /// The stream to write the WAV into. Closed on exit. /// The lump containing the sample data /// /// Not a sample lump /// 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 /// Converts a WAV file to a sample lump /// /// The stream containing the WAV. Closed on exit. /// The lump /// /// Must be a WAV, uncompressed, mono, 8-bits and less than 65535 samples /// 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