// 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