diff options
-rw-r--r-- | AssemblyInfo.cs | 32 | ||||
-rw-r--r-- | DOOM.resources | bin | 0 -> 1575 bytes | |||
-rw-r--r-- | DoomException.cs | 35 | ||||
-rw-r--r-- | Noddybox.DOOM.cmbx | 16 | ||||
-rw-r--r-- | Noddybox.DOOM.prjx | 30 | ||||
-rw-r--r-- | WAD.cs | 365 | ||||
-rw-r--r-- | WADCollection.cs | 196 | ||||
-rw-r--r-- | WADEntry.cs | 143 | ||||
-rw-r--r-- | WADEntryConvert.cs | 685 | ||||
-rw-r--r-- | palette.bmp | bin | 0 -> 1082 bytes |
10 files changed, 1502 insertions, 0 deletions
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..6566edf --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes specify the key for the sign of your assembly. See the +// .NET Framework documentation for more information about signing. +// This is not required, if you don't want signing let these attributes like they're. +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] diff --git a/DOOM.resources b/DOOM.resources Binary files differnew file mode 100644 index 0000000..8a54a4c --- /dev/null +++ b/DOOM.resources diff --git a/DoomException.cs b/DoomException.cs new file mode 100644 index 0000000..e2777d8 --- /dev/null +++ b/DoomException.cs @@ -0,0 +1,35 @@ +// 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; + +namespace Noddybox.DOOM +{ + /// <summary> + /// Namespace exception + /// </summary> + public class DoomException : Exception + { + /// <summary> + /// Base exception for the namespace + /// </summary> + /// <param name="message">Error message</param> + public DoomException(string message) : base(message) + { + } + } +} diff --git a/Noddybox.DOOM.cmbx b/Noddybox.DOOM.cmbx new file mode 100644 index 0000000..768070a --- /dev/null +++ b/Noddybox.DOOM.cmbx @@ -0,0 +1,16 @@ +<Combine fileversion="1.0" name="Noddybox.DOOM" description=""> + <StartMode startupentry="Noddybox.DOOM" single="True"> + <Execute entry="Noddybox.DOOM" type="None" /> + </StartMode> + <Entries> + <Entry filename=".\.\Noddybox.DOOM.prjx" /> + </Entries> + <Configurations active="Debug"> + <Configuration name="Release"> + <Entry name="Noddybox.DOOM" configurationname="Debug" build="False" /> + </Configuration> + <Configuration name="Debug"> + <Entry name="Noddybox.DOOM" configurationname="Debug" build="False" /> + </Configuration> + </Configurations> +</Combine>
\ No newline at end of file diff --git a/Noddybox.DOOM.prjx b/Noddybox.DOOM.prjx new file mode 100644 index 0000000..6de2c54 --- /dev/null +++ b/Noddybox.DOOM.prjx @@ -0,0 +1,30 @@ +<Project name="Noddybox.DOOM" standardNamespace="Noddybox.DOOM" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#"> + <Contents> + <File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" /> + <File name=".\DoomException.cs" subtype="Code" buildaction="Compile" dependson="" data="" /> + <File name=".\WAD.cs" subtype="Code" buildaction="Compile" dependson="" data="" /> + <File name=".\WADEntry.cs" subtype="Code" buildaction="Compile" dependson="" data="" /> + <File name=".\WADCollection.cs" subtype="Code" buildaction="Compile" dependson="" data="" /> + <File name=".\WADEntryConvert.cs" subtype="Code" buildaction="Compile" dependson="" data="" /> + <File name=".\DOOM.resources" subtype="Code" buildaction="EmbedAsResource" dependson="" data="" /> + </Contents> + <References /> + <DeploymentInformation target="" script="" strategy="File" /> + <Configuration runwithwarnings="True" name="Debug"> + <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="Standard" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="True" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" /> + <Execution commandlineparameters="" consolepause="False" /> + <Output directory=".\bin\Debug" assembly="Noddybox.DOOM" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" /> + </Configuration> + <Configurations active="Debug"> + <Configuration runwithwarnings="True" name="Debug"> + <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="Standard" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="True" generateoverflowchecks="True" mainclass="" target="Library" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" /> + <Execution commandlineparameters="" consolepause="False" /> + <Output directory=".\bin\Debug" assembly="Noddybox.DOOM" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" /> + </Configuration> + <Configuration runwithwarnings="True" name="Release"> + <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="False" optimize="True" unsafecodeallowed="True" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="" generatexmldocumentation="True" win32Icon="" noconfig="False" nostdlib="False" /> + <Execution commandlineparameters="" consolepause="False" /> + <Output directory=".\bin\Release" assembly="Noddybox.DOOM" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" /> + </Configuration> + </Configurations> +</Project>
\ No newline at end of file @@ -0,0 +1,365 @@ +// 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.IO; +using System.Collections; +using System.Text; + +namespace Noddybox.DOOM +{ + /// <summary> + /// Defines a WAD File + /// </summary> + public class WAD : IEnumerable, IEnumerator + { + // ==================================== + // PUBLIC + // ==================================== + + /// <summary> + /// Describes the type of WAD + /// </summary> + public enum EWadType + { + /// <summary> + /// An Interal WAD (eg. DOOM 2) + /// </summary> + IWAD, + + /// <summary> + /// A Patch WAD (eg. a user-defined level) + /// </summary> + PWAD + }; + + /// <summary> + /// Construct a new, empty WAD file + /// </summary> + public WAD() + { + m_type=EWadType.PWAD; + m_entry=new ArrayList(); + m_ascii=new ASCIIEncoding(); + } + + /// <summary> + /// Load a WAD file from disk + /// </summary> + /// <param name="path">The WAD file</param> + /// <exception cref="System.IO.IOException"> + /// I/O errors + /// </exception> + public WAD(string path) + { + m_path=path; + m_entry=new ArrayList(); + m_ascii=new ASCIIEncoding(); + Load(); + } + + /// <summary> + /// Get/set the path of the WAD file on disk + /// </summary> + public string Path + { + get {return m_path;} + set {m_path=value;} + } + + /// <summary> + /// Get the type of WAD file + /// </summary> + public EWadType WadType + { + get {return m_type;} + } + + /// <summary> + /// Adds a new lump to the WAD + /// </summary> + /// <param name="e">The lump</param> + public void AddEntry(WADEntry e) + { + m_entry.Add(e); + } + + /// <summary> + /// Removes a lump from the WAD + /// </summary> + /// <param name="e">The lump</param> + public void RemoveEntry(WADEntry e) + { + m_entry.Remove(e); + } + + /// <summary> + /// Find a lump + /// </summary> + /// <param name="name">The name to find</param> + /// <returns>The first lump that matches the name, or null for no match</returns> + public WADEntry FindEntry(string name) + { + foreach (WADEntry e in m_entry) + { + if (e.Name==name) + { + return e; + } + } + + return null; + } + + /// <summary> + /// Load a WAD file + /// </summary> + /// <exception cref="System.IO.IOException"> + /// I/O errors + /// </exception> + public void Load() + { + if (m_path==null) + { + throw new DoomException("Tried to load a WAD without a path"); + } + + FileStream fs=File.OpenRead(m_path); + + string id=ReadString(fs,4); + + if (id=="IWAD") + { + m_type=EWadType.IWAD; + } + else if (id=="PWAD") + { + m_type=EWadType.PWAD; + } + else + { + fs.Close(); + throw new DoomException("Not a WAD File - " + m_path); + } + + m_entry.Clear(); + + int num=ReadLong(fs); + + if (num>0) + { + fs.Position=ReadLong(fs); + + for(int f=0;f<num;f++) + { + int off=ReadLong(fs); + int len=ReadLong(fs); + string name=ReadString(fs,8); + + long cur=fs.Position; + + byte[] data=new byte[len]; + + fs.Position=off; + fs.Read(data,0,len); + fs.Position=cur; + + m_entry.Add(new WADEntry(name,data)); + } + } + + fs.Close(); + } + + /// <summary> + /// Saves the WAD file + /// </summary> + /// <exception cref="Noddybox.DOOM.DoomException"> + /// Tried to save an IWAD file + /// </exception> + /// <exception cref="System.IO.IOException"> + /// I/O errors + /// </exception> + public void Save() + { + if (m_path==null) + { + throw new DoomException("Tried to save a WAD without a path"); + } + + if (m_type==EWadType.IWAD) + { + throw new DoomException("Tried to save an IWAD"); + } + + FileStream fs=File.Create(m_path); + + int len=0; + + foreach (WADEntry e in this) + { + len+=e.Length; + } + + WriteString(fs,"PWAD",4); + WriteLong(fs,m_entry.Count); + + int offset=12; + + WriteLong(fs,len+offset); + + foreach (WADEntry e in m_entry) + { + fs.Write(e.Data,0,e.Length); + } + + foreach (WADEntry e in m_entry) + { + WriteLong(fs,offset); + WriteLong(fs,e.Length); + WriteString(fs,e.Name,8); + offset+=e.Length; + } + + fs.Close(); + } + + /// <summary> + /// Returns the number of lumps in the WAD + /// </summary> + public int Count + { + get {return m_entry.Count;} + } + + /// <summary> + /// Index into the lumps + /// </summary> + public WADEntry this[int index] + { + get {return (WADEntry)m_entry[index];} + } + + /// <summary> + /// Gets an enumerator + /// </summary> + /// <returns>The enumerator</returns> + public IEnumerator GetEnumerator() + { + Reset(); + return this; + } + + /// <summary> + /// Get the current object from the enumerator + /// </summary> + public object Current + { + get + { + if (m_iter_index==-1) + { + throw new InvalidOperationException(); + } + + return m_entry[m_iter_index]; + } + } + + /// <summary> + /// Reset the enumerator to the start of the collection. + /// </summary> + public void Reset() + { + m_iter_index=-1; + } + + /// <summary> + /// Move the enumerator to the next item. + /// </summary> + /// <returns>True if there are more objects to enumerate over</returns> + public bool MoveNext() + { + m_iter_index++; + + if (m_iter_index>=m_entry.Count) + { + m_iter_index=-1; + } + + return m_iter_index!=-1; + } + + // ==================================== + // PRIVATE + // ==================================== + private string m_path; + private EWadType m_type; + private ArrayList m_entry; + private ASCIIEncoding m_ascii; + + private int m_iter_index; + + private int ReadLong(FileStream fs) + { + int l=0; + + for(int f=0;f<4;f++) + { + int b=fs.ReadByte(); + l|=b<<(f*8); + } + + return l; + } + + private string ReadString(FileStream fs, int len) + { + byte[] data=new byte[len]; + + fs.Read(data,0,len); + + return m_ascii.GetString(data,0,len); + } + + private void WriteLong(FileStream fs, int l) + { + for(int f=0;f<4;f++) + { + fs.WriteByte((byte)(l&0xff)); + l=l>>8; + } + } + + private void WriteString(FileStream fs, string s, int len) + { + byte[] data=m_ascii.GetBytes(s); + + for(int f=0;f<len;f++) + { + if (f<data.Length) + { + fs.WriteByte(data[f]); + } + else + { + fs.WriteByte(0); + } + } + } + } +} diff --git a/WADCollection.cs b/WADCollection.cs new file mode 100644 index 0000000..ba6eb5b --- /dev/null +++ b/WADCollection.cs @@ -0,0 +1,196 @@ +// 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; + +namespace Noddybox.DOOM +{ + /// <summary> + /// Performs operations on a collection of WADs + /// </summary> + public class WADCollection : IEnumerable, IEnumerator + { + // ==================================== + // PUBLIC + // ==================================== + + /// <summary> + /// Delegate for WAD added event + /// </summary> + public delegate void WadAddedEvent(WADCollection collection, WAD wad); + + /// <summary> + /// Delegate for WAD removed event + /// </summary> + public delegate void WadRemovedEvent(WADCollection collection); + + /// <summary> + /// Create an empty collection of WADs + /// </summary> + public WADCollection() + { + m_wads=new ArrayList(); + m_iter_index=-1; + } + + /// <summary> + /// Add a WAD to the collection + /// </summary> + /// <param name="w">The WAD</param> + public void AddWad(WAD w) + { + m_wads.Add(w); + + if (WadAdded!=null) + { + WadAdded(this,w); + } + } + + /// <summary> + /// Remove a WAD from the collection + /// </summary> + /// <param name="w">The WAD</param> + public void RemoveWad(WAD w) + { + m_wads.Remove(w); + + if (WadRemoved!=null) + { + WadRemoved(this); + } + } + + + /// <summary> + /// Get the first palette (PLAYPAL) lump from the WAD files + /// </summary> + /// <returns>The lump containing the palette, or null if not found</returns> + public WADEntry GetPalette() + { + return QueryDirectory("PLAYPAL"); + } + + + /// <summary> + /// Queries the WAD directories for the named lump. Note it searches + /// back from the last loaded WAD as DOOM does. + /// </summary> + /// <param name="name">The lump name</param> + /// <returns>The lump, or null if it doesn't exist</returns> + public WADEntry QueryDirectory(string name) + { + for(int f=m_wads.Count-1;f>=0;f--) + { + WAD w=(WAD)m_wads[f]; + + WADEntry e=w.FindEntry("PLAYPAL"); + + if (e!=null) + { + return e; + } + } + + return null; + } + + /// <summary> + /// Called when a WAD is added to the collection + /// </summary> + public event WadAddedEvent WadAdded; + + /// <summary> + /// Called when a WAD is removed from the collection + /// </summary> + public event WadRemovedEvent WadRemoved; + + /// <summary> + /// Returns the number of WADs in the collection + /// </summary> + public int Count + { + get {return m_wads.Count;} + } + + /// <summary> + /// Index into the WADs + /// </summary> + public WAD this[int index] + { + get {return (WAD)m_wads[index];} + } + + /// <summary> + /// Gets an enumerator + /// </summary> + /// <returns>The enumerator</returns> + public IEnumerator GetEnumerator() + { + Reset(); + return this; + } + + /// <summary> + /// Get the current object from the enumerator + /// </summary> + public object Current + { + get + { + if (m_iter_index==-1) + { + throw new InvalidOperationException(); + } + + return m_wads[m_iter_index]; + } + } + + /// <summary> + /// Reset the enumerator to the start of the collection. + /// </summary> + public void Reset() + { + m_iter_index=-1; + } + + /// <summary> + /// Move the enumerator to the next item. + /// </summary> + /// <returns>True if there are more objects to enumerate over</returns> + public bool MoveNext() + { + m_iter_index++; + + if (m_iter_index>=m_wads.Count) + { + m_iter_index=-1; + } + + return m_iter_index!=-1; + } + + + // ==================================== + // PRIVATE + // ==================================== + private ArrayList m_wads; + private int m_iter_index; + } +} diff --git a/WADEntry.cs b/WADEntry.cs new file mode 100644 index 0000000..d43da68 --- /dev/null +++ b/WADEntry.cs @@ -0,0 +1,143 @@ +// 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.IO; + +namespace Noddybox.DOOM +{ + /// <summary> + /// Describes a directory entry in a WAD file + /// </summary> + public class WADEntry + { + // ==================================== + // PUBLIC + // ==================================== + + /// <summary> + /// Create an empty lump + /// </summary> + public WADEntry() + { + Name="LUMP"; + Data=null; + } + + /// <summary> + /// Create a lump + /// </summary> + /// <param name="name">The name</param> + /// <param name="data">The data</param> + public WADEntry(string name, byte[] data) + { + Name=name; + Data=data; + } + + /// <summary> + /// Create a lump + /// </summary> + /// <param name="we">The lump to copy</param> + public WADEntry(WADEntry we) + { + Name=we.Name; + Data=(byte[])we.Data.Clone(); + } + + /// <summary> + /// Get/set the lump's name + /// </summary> + public string Name + { + get {return m_name;} + set + { + m_name=value; + + if (m_name.Length>8) + { + m_name=m_name.Substring(0,8); + } + + m_name=m_name.Trim(new char[] {'\0'}); + } + } + + /// <summary> + /// Get the length of the lump's data + /// </summary> + public int Length + { + get {return m_length;} + } + + /// <summary> + /// Get/set the lump data + /// </summary> + public byte[] Data + { + get {return m_data;} + set + { + m_data=value; + + if (m_data!=null) + { + m_length=m_data.Length; + } + else + { + m_length=0; + } + } + } + + /// <summary> + /// Index into the lump data as bytes + /// </summary> + public byte this[int index] + { + get {return m_data[index];} + set {m_data[index]=value;} + } + + /// <summary> + /// Index into the lump data as bytes + /// </summary> + public byte this[uint index] + { + get {return m_data[index];} + set {m_data[index]=value;} + } + + /// <summary> + /// Get the lump data as a MemoryStream + /// </summary> + public MemoryStream Stream + { + get {return new MemoryStream(m_data);} + } + + // ==================================== + // PRIVATE + // ==================================== + private string m_name; + private int m_length; + private byte[] m_data; + } +} 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); + } + } +} diff --git a/palette.bmp b/palette.bmp Binary files differnew file mode 100644 index 0000000..1d46c83 --- /dev/null +++ b/palette.bmp |