loading resources from PK3 files implemented (tested with KDIZD, CAH and SP_USIMP)

This commit is contained in:
codeimp 2009-01-03 19:45:59 +00:00
parent 4ce26ad8fa
commit 320a002677
16 changed files with 844 additions and 376 deletions

View file

@ -650,6 +650,8 @@
<None Include="Resources\Cut.png" />
<None Include="Resources\Close.png" />
<Compile Include="Config\FlagTranslation.cs" />
<Compile Include="Data\PK3FileImage.cs" />
<Compile Include="Data\PK3StructuredReader.cs" />
<Compile Include="Editing\EditingManager.cs" />
<EmbeddedResource Include="Resources\Crosshair.png" />
<EmbeddedResource Include="Resources\CrosshairBusy.png" />

View file

@ -243,9 +243,11 @@ namespace CodeImp.DoomBuilder.Data
break;
}
}
catch(Exception)
catch(Exception e)
{
// Unable to load resource
General.WriteLogLine("ERROR while creating data reader. " + e.GetType().Name + ": " + e.Message);
General.WriteLogLine(e.StackTrace);
General.ShowErrorMessage("Unable to load resources from location \"" + dl.location + "\". Please make sure the location is accessible and not in use by another program.", MessageBoxButtons.OK);
continue;
}
@ -361,9 +363,11 @@ namespace CodeImp.DoomBuilder.Data
General.WriteLogLine("Resumed data resource '" + d.Location.location + "'");
d.Resume();
}
catch(Exception)
catch(Exception e)
{
// Unable to load resource
General.WriteLogLine("ERROR while resuming data reader. " + e.GetType().Name + ": " + e.Message);
General.WriteLogLine(e.StackTrace);
General.ShowErrorMessage("Unable to load resources from location \"" + d.Location.location + "\". Please make sure the location is accessible and not in use by another program.", MessageBoxButtons.OK);
}
}
@ -891,7 +895,7 @@ namespace CodeImp.DoomBuilder.Data
string[] files = Directory.GetFiles(General.SpritesPath, "*.png", SearchOption.TopDirectoryOnly);
foreach(string spritefile in files)
{
ImageData img = new FileImage(Path.GetFileNameWithoutExtension(spritefile).ToLowerInvariant(), spritefile);
ImageData img = new FileImage(Path.GetFileNameWithoutExtension(spritefile).ToLowerInvariant(), spritefile, false);
img.LoadImage();
internalsprites.Add(img.Name, img);
}

View file

@ -30,66 +30,17 @@ using CodeImp.DoomBuilder.IO;
namespace CodeImp.DoomBuilder.Data
{
internal sealed class DirectoryReader : DataReader
internal sealed class DirectoryReader : PK3StructuredReader
{
#region ================== Constants
private const string PATCHES_DIR = "patches";
private const string TEXTURES_DIR = "textures";
private const string FLATS_DIR = "flats";
private const string HIRES_DIR = "hires";
private const string SPRITES_DIR = "sprites";
#endregion
#region ================== Variables
// Source
private bool roottextures;
private bool rootflats;
// Paths
private string rootpath;
private string patchespath;
private string texturespath;
private string flatspath;
private string hirespath;
private string spritespath;
// WAD files that must be loaded as well
private List<WADReader> wads;
#endregion
#region ================== Properties
#endregion
#region ================== Constructor / Disposer
// Constructor
public DirectoryReader(DataLocation dl) : base(dl)
{
// Initialize
this.roottextures = dl.textures;
this.rootflats = dl.flats;
this.rootpath = dl.location;
this.patchespath = Path.Combine(rootpath, PATCHES_DIR);
this.texturespath = Path.Combine(rootpath, TEXTURES_DIR);
this.flatspath = Path.Combine(rootpath, FLATS_DIR);
this.hirespath = Path.Combine(rootpath, HIRES_DIR);
this.spritespath = Path.Combine(rootpath, SPRITES_DIR);
General.WriteLogLine("Opening directory resource '" + location.location + "'");
// Load all WAD files in the root as WAD resources
string[] wadfiles = Directory.GetFiles(rootpath, "*.wad", SearchOption.TopDirectoryOnly);
wads = new List<WADReader>(wadfiles.Length);
foreach(string w in wadfiles)
{
DataLocation wdl = new DataLocation(DataLocation.RESOURCE_WAD, w, false, false);
wads.Add(new WADReader(wdl));
}
// Initialize
Initialize(dl.location);
// We have no destructor
GC.SuppressFinalize(this);
@ -101,9 +52,6 @@ namespace CodeImp.DoomBuilder.Data
// Not already disposed?
if(!isdisposed)
{
// Clean up
foreach(WADReader wr in wads) wr.Dispose();
General.WriteLogLine("Closing directory resource '" + location.location + "'");
// Done
@ -113,275 +61,62 @@ namespace CodeImp.DoomBuilder.Data
#endregion
#region ================== Management
// This suspends use of this resource
public override void Suspend()
{
foreach(WADReader wr in wads) wr.Suspend();
base.Suspend();
}
// This resumes use of this resource
public override void Resume()
{
foreach(WADReader wr in wads) wr.Resume();
base.Resume();
}
#endregion
#region ================== Palette
// This loads the PLAYPAL palette
public override Playpal LoadPalette()
{
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Palette from wad(s)
Playpal palette = null;
foreach(WADReader wr in wads)
{
Playpal wadpalette = wr.LoadPalette();
if(wadpalette != null) palette = wadpalette;
}
// Done
return palette;
}
#endregion
#region ================== Textures
// This loads the textures
public override ICollection<ImageData> LoadTextures(PatchNames pnames)
{
List<ImageData> images = new List<ImageData>();
ICollection<ImageData> collection;
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Load from wad files (NOTE: backward order, because the last wad's images have priority)
for(int i = wads.Count - 1; i >= 0; i--)
{
collection = wads[i].LoadTextures(pnames);
AddImagesToList(images, collection);
}
// Should we load the images in this directory as textures?
if(roottextures)
{
collection = LoadDirectoryImages(rootpath);
AddImagesToList(images, collection);
}
// TODO: Add support for hires texture here
// Add images from texture directory
collection = LoadDirectoryImages(texturespath);
AddImagesToList(images, collection);
return images;
}
// This returns the patch names from the PNAMES lump
// A directory resource does not support this lump, but the wads in the directory may contain this lump
public override PatchNames LoadPatchNames()
{
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Load from wad files (NOTE: backward order, because the last wad's images have priority)
for(int i = wads.Count - 1; i >= 0; i--)
{
PatchNames pnames = wads[i].LoadPatchNames();
if(pnames != null) return pnames;
}
return null;
}
// This finds and returns a patch stream
public override Stream GetPatchData(string pname)
{
string filename;
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Find in any of the wad files
for(int i = wads.Count - 1; i >= 0; i--)
{
Stream data = wads[i].GetPatchData(pname);
if(data != null) return data;
}
// Find in patches directory
string datafile = null;
filename = Path.Combine(patchespath, pname + ".bmp");
if(File.Exists(filename)) datafile = filename;
filename = Path.Combine(patchespath, pname + ".gif");
if(File.Exists(filename)) datafile = filename;
filename = Path.Combine(patchespath, pname + ".png");
if(File.Exists(filename)) datafile = filename;
filename = Path.Combine(patchespath, pname);
if(File.Exists(filename)) datafile = filename;
// Found anything?
if(datafile != null)
{
byte[] filedata = File.ReadAllBytes(datafile);
MemoryStream mem = new MemoryStream(filedata);
return mem;
}
// Nothing found
return null;
}
#endregion
#region ================== Flats
// This loads the textures
public override ICollection<ImageData> LoadFlats()
{
List<ImageData> images = new List<ImageData>();
ICollection<ImageData> collection;
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Load from wad files (NOTE: backward order, because the last wad's images have priority)
for(int i = wads.Count - 1; i >= 0; i--)
{
collection = wads[i].LoadFlats();
AddImagesToList(images, collection);
}
// Should we load the images in this directory as flats?
if(rootflats)
{
collection = LoadDirectoryImages(rootpath);
AddImagesToList(images, collection);
}
// Add images from flats directory
collection = LoadDirectoryImages(flatspath);
AddImagesToList(images, collection);
return images;
}
#endregion
#region ================== Sprites
// This finds and returns a sprite stream
public override Stream GetSpriteData(string pname)
{
string pfilename = pname.Replace('\\', '^');
string filename;
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Find in any of the wad files
for(int i = wads.Count - 1; i >= 0; i--)
{
Stream sprite = wads[i].GetSpriteData(pname);
if(sprite != null) return sprite;
}
// Find in sprites directory
string spritefoundfile = null;
filename = Path.Combine(spritespath, pfilename + ".bmp");
if(File.Exists(filename)) spritefoundfile = filename;
filename = Path.Combine(spritespath, pfilename + ".gif");
if(File.Exists(filename)) spritefoundfile = filename;
filename = Path.Combine(spritespath, pfilename + ".png");
if(File.Exists(filename)) spritefoundfile = filename;
// Found anything?
if(spritefoundfile != null)
{
byte[] filedata = File.ReadAllBytes(spritefoundfile);
MemoryStream mem = new MemoryStream(filedata);
return mem;
}
// Nothing found
return null;
}
#endregion
#region ================== Methods
// This loads the images in this directory
private ICollection<ImageData> LoadDirectoryImages(string path)
// This creates an image
protected override ImageData CreateImage(string name, string filename, bool flat)
{
List<ImageData> images = new List<ImageData>();
string[] files;
string name;
return new FileImage(name, filename, flat);
}
// Find all BMP files
files = Directory.GetFiles(path, "*.bmp", SearchOption.TopDirectoryOnly);
// This returns true if the specified file exists
protected override bool FileExists(string filename)
{
return File.Exists(filename);
}
// This returns all files in a given directory
protected override string[] GetAllFiles(string path)
{
return Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly);
}
// Find all GIF files and append to files array
AddToArray(ref files, Directory.GetFiles(path, "*.gif", SearchOption.TopDirectoryOnly));
// This returns all files in a given directory that match the given extension
protected override string[] GetFilesWithExt(string path, string extension)
{
return Directory.GetFiles(path, "*." + extension, SearchOption.TopDirectoryOnly);
}
// Find all PNG files and append to files array
AddToArray(ref files, Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly));
// Go for all files
// This finds the first file that has the specific name, regardless of file extension
protected override string FindFirstFile(string path, string beginswith)
{
string[] files = GetAllFiles(path);
foreach(string f in files)
{
// Make the texture name from filename without extension
name = Path.GetFileNameWithoutExtension(f).ToUpperInvariant();
if(name.Length > 8) name = name.Substring(0, 8);
// Add image to list
images.Add(new FileImage(name, f));
if(string.Compare(Path.GetFileNameWithoutExtension(f), beginswith, true) == 0)
return f;
}
// Return result
return images;
return null;
}
// This resizes a string array and adds to it
private void AddToArray(ref string[] array, string[] add)
// This loads an entire file in memory and returns the stream
// NOTE: Callers are responsible for disposing the stream!
protected override MemoryStream LoadFile(string filename)
{
int insertindex = array.Length;
Array.Resize<string>(ref array, array.Length + add.Length);
add.CopyTo(array, insertindex);
return new MemoryStream(File.ReadAllBytes(filename));
}
// This copies images from a collection unless they already exist in the list
private void AddImagesToList(List<ImageData> targetlist, ICollection<ImageData> sourcelist)
// This creates a temp file for the speciied file and return the absolute path to the temp file
// NOTE: Callers are responsible for removing the temp file when done!
protected override string CreateTempFile(string filename)
{
// Go for all source images
foreach(ImageData src in sourcelist)
{
// Check if exists in target list
bool alreadyexists = false;
foreach(ImageData tgt in targetlist)
{
if(tgt.LongName == src.LongName)
{
alreadyexists = true;
break;
}
}
// Add source image to target list
if(!alreadyexists) targetlist.Add(src);
}
// Just copy the file
string tempfile = General.MakeTempFilename(General.Map.TempPath, "wad");
File.Copy(filename, tempfile);
return tempfile;
}
#endregion
}
}

View file

@ -24,6 +24,7 @@ using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using CodeImp.DoomBuilder.IO;
#endregion
@ -34,34 +35,56 @@ namespace CodeImp.DoomBuilder.Data
#region ================== Variables
private string filepathname;
private int probableformat;
private float scalex;
private float scaley;
#endregion
#region ================== Constructor / Disposer
// Constructor
public FileImage(string name, string filepathname)
public FileImage(string name, string filepathname, bool asflat)
{
// Initialize
this.filepathname = filepathname;
SetName(name);
// Temporarily load file
Bitmap bmp = (Bitmap)Bitmap.FromFile(filepathname);
// Get width and height from image
width = bmp.Size.Width;
height = bmp.Size.Height;
scaledwidth = (float)bmp.Size.Width * General.Map.Config.DefaultTextureScale;
scaledheight = (float)bmp.Size.Height * General.Map.Config.DefaultTextureScale;
// Unload file
bmp.Dispose();
if(asflat)
{
probableformat = ImageDataFormat.DOOMFLAT;
scalex = General.Map.Config.DefaultFlatScale;
scaley = General.Map.Config.DefaultFlatScale;
}
else
{
probableformat = ImageDataFormat.DOOMPICTURE;
scalex = General.Map.Config.DefaultTextureScale;
scaley = General.Map.Config.DefaultTextureScale;
}
// We have no destructor
GC.SuppressFinalize(this);
}
// Constructor
public FileImage(string name, string filepathname, bool asflat, float scalex, float scaley)
{
// Initialize
this.filepathname = filepathname;
this.scalex = scalex;
this.scaley = scaley;
SetName(name);
if(asflat)
probableformat = ImageDataFormat.DOOMFLAT;
else
probableformat = ImageDataFormat.DOOMPICTURE;
// We have no destructor
GC.SuppressFinalize(this);
}
#endregion
#region ================== Methods
@ -74,17 +97,41 @@ namespace CodeImp.DoomBuilder.Data
lock(this)
{
// Load file
if(bitmap != null) bitmap.Dispose();
bitmap = (Bitmap)Bitmap.FromFile(filepathname);
// Load file data
if(bitmap != null) bitmap.Dispose(); bitmap = null;
MemoryStream filedata = new MemoryStream(File.ReadAllBytes(filepathname));
// Get a reader for the data
IImageReader reader = ImageDataFormat.GetImageReader(filedata, probableformat, General.Map.Data.Palette);
if(!(reader is UnknownImageReader))
{
// Load the image
filedata.Seek(0, SeekOrigin.Begin);
try { bitmap = reader.ReadAsBitmap(filedata); }
catch(InvalidDataException)
{
// Data cannot be read!
bitmap = null;
}
}
// Not loaded?
if(bitmap == null)
{
General.WriteLogLine("WARNING: Image file '" + filepathname + "' data format could not be read, while loading texture '" + this.Name + "'!");
loadfailed = true;
filedata.Dispose();
return;
}
// Get width and height from image
width = bitmap.Size.Width;
height = bitmap.Size.Height;
scaledwidth = (float)bitmap.Size.Width * General.Map.Config.DefaultTextureScale;
scaledheight = (float)bitmap.Size.Height * General.Map.Config.DefaultTextureScale;
scaledwidth = (float)bitmap.Size.Width * scalex;
scaledheight = (float)bitmap.Size.Height * scaley;
// Pass on to base
filedata.Dispose();
base.LocalLoadImage();
}
}

View file

@ -192,7 +192,7 @@ namespace CodeImp.DoomBuilder.Data
int oldheight = height;
float oldscaledwidth = scaledwidth;
float oldscaledheight = scaledheight;
// Do the loading
LocalLoadImage();

125
Source/Data/PK3FileImage.cs Normal file
View file

@ -0,0 +1,125 @@
#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* 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.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using CodeImp.DoomBuilder.IO;
#endregion
namespace CodeImp.DoomBuilder.Data
{
public sealed class PK3FileImage : ImageData
{
#region ================== Variables
private PK3Reader datareader;
private string filepathname;
private int probableformat;
private float scalex;
private float scaley;
#endregion
#region ================== Constructor / Disposer
// Constructor
internal PK3FileImage(PK3Reader datareader, string name, string filepathname, bool asflat)
{
// Initialize
this.datareader = datareader;
this.filepathname = filepathname;
SetName(name);
if(asflat)
{
probableformat = ImageDataFormat.DOOMFLAT;
scalex = General.Map.Config.DefaultFlatScale;
scaley = General.Map.Config.DefaultFlatScale;
}
else
{
probableformat = ImageDataFormat.DOOMPICTURE;
scalex = General.Map.Config.DefaultTextureScale;
scaley = General.Map.Config.DefaultTextureScale;
}
// We have no destructor
GC.SuppressFinalize(this);
}
#endregion
#region ================== Methods
// This loads the image
protected override void LocalLoadImage()
{
// Leave when already loaded
if(this.IsImageLoaded) return;
lock(this)
{
// Load file data
if(bitmap != null) bitmap.Dispose(); bitmap = null;
MemoryStream filedata = datareader.ExtractFile(filepathname);
// Get a reader for the data
IImageReader reader = ImageDataFormat.GetImageReader(filedata, probableformat, General.Map.Data.Palette);
if(!(reader is UnknownImageReader))
{
// Load the image
filedata.Seek(0, SeekOrigin.Begin);
try { bitmap = reader.ReadAsBitmap(filedata); }
catch(InvalidDataException)
{
// Data cannot be read!
bitmap = null;
}
}
// Not loaded?
if(bitmap == null)
{
General.WriteLogLine("WARNING: Image file '" + filepathname + "' data format could not be read, while loading texture '" + this.Name + "'!");
loadfailed = true;
filedata.Dispose();
return;
}
// Get width and height from image
width = bitmap.Size.Width;
height = bitmap.Size.Height;
scaledwidth = (float)bitmap.Size.Width * scalex;
scaledheight = (float)bitmap.Size.Height * scaley;
// Pass on to base
filedata.Dispose();
base.LocalLoadImage();
}
}
#endregion
}
}

View file

@ -31,17 +31,11 @@ using ICSharpCode.SharpZipLib.Zip;
namespace CodeImp.DoomBuilder.Data
{
internal sealed class PK3Reader : DataReader
internal sealed class PK3Reader : PK3StructuredReader
{
#region ================== Constants
#endregion
#region ================== Variables
#endregion
#region ================== Properties
private List<string> fileslist;
#endregion
@ -50,21 +44,29 @@ namespace CodeImp.DoomBuilder.Data
// Constructor
public PK3Reader(DataLocation dl) : base(dl)
{
// Initialize
General.WriteLogLine("Opening PK3 resource '" + location.location + "'");
//TEST
/*
ZipInputStream z = new ZipInputStream(File.Open(dl.location, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
ZipEntry ze;
while((ze = z.GetNextEntry()) != null)
{
General.WriteLogLine(ze.Name);
}
z.Dispose();
*/
// Open the zip file
ZipInputStream zipstream = OpenPK3File();
// Make list of all files
fileslist = new List<string>();
ZipEntry entry = zipstream.GetNextEntry();
while(entry != null)
{
if(entry.IsFile) fileslist.Add(entry.Name.ToLowerInvariant());
// Next
entry = zipstream.GetNextEntry();
}
// Done with the zip file
zipstream.Close();
zipstream.Dispose();
// Initialize without path (because we use paths relative to the PK3 file)
Initialize("");
// We have no destructor
GC.SuppressFinalize(this);
}
@ -76,9 +78,7 @@ namespace CodeImp.DoomBuilder.Data
if(!isdisposed)
{
General.WriteLogLine("Closing PK3 resource '" + location.location + "'");
// Clean up
// Done
base.Dispose();
}
@ -86,22 +86,148 @@ namespace CodeImp.DoomBuilder.Data
#endregion
#region ================== Palette
#region ================== Management
// This loads the PLAYPAL palette
public override Playpal LoadPalette()
// This opens the zip file for reading
private ZipInputStream OpenPK3File()
{
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Not yet implemented
return null;
FileStream filestream = File.Open(location.location, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
filestream.Seek(0, SeekOrigin.Begin);
return new ZipInputStream(filestream);
}
#endregion
#region ================== Methods
// This creates an image
protected override ImageData CreateImage(string name, string filename, bool flat)
{
return new PK3FileImage(this, name, filename, flat);
}
#region ================== Textures
// This returns true if the specified file exists
protected override bool FileExists(string filename)
{
string lowfile = filename.ToLowerInvariant();
foreach(string f in fileslist)
{
if((string.Compare(f, lowfile) == 0)) return true;
}
return false;
}
// This returns all files in a given directory
protected override string[] GetAllFiles(string path)
{
List<string> matches = new List<string>();
string lowpath = path.ToLowerInvariant();
foreach(string f in fileslist)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) &&
(Path.GetFileName(f).Length > 0))
matches.Add(f);
}
return matches.ToArray();
}
// This returns all files in a given directory that match the given extension
protected override string[] GetFilesWithExt(string path, string extension)
{
List<string> matches = new List<string>();
string lowpath = path.ToLowerInvariant();
string lowext = "." + extension.ToLowerInvariant();
foreach(string f in fileslist)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) && f.EndsWith(lowext))
matches.Add(f);
}
return matches.ToArray();
}
// This finds the first file that has the specific name, regardless of file extension
protected override string FindFirstFile(string path, string beginswith)
{
string lowpath = path.ToLowerInvariant();
string lowbegin = beginswith.ToLowerInvariant();
foreach(string f in fileslist)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) &&
(string.Compare(Path.GetFileNameWithoutExtension(f), lowbegin) == 0))
return f;
}
return null;
}
// This loads an entire file in memory and returns the stream
// NOTE: Callers are responsible for disposing the stream!
protected override MemoryStream LoadFile(string filename)
{
MemoryStream filedata = null;
byte[] copybuffer = new byte[4096];
// Open the zip file
ZipInputStream zipstream = OpenPK3File();
ZipEntry entry = zipstream.GetNextEntry();
while(entry != null)
{
// Is this the entry we are looking for?
if(string.Compare(entry.Name, filename, true) == 0)
{
int expectedsize = (int)entry.Size;
if(expectedsize < 1) expectedsize = 1024;
filedata = new MemoryStream(expectedsize);
int readsize = zipstream.Read(copybuffer, 0, copybuffer.Length);
while(readsize > 0)
{
filedata.Write(copybuffer, 0, readsize);
readsize = zipstream.Read(copybuffer, 0, copybuffer.Length);
}
break;
}
// Next
entry = zipstream.GetNextEntry();
}
// Done with the zip file
zipstream.Close();
zipstream.Dispose();
// Nothing found?
if(filedata == null)
{
throw new FileNotFoundException("Cannot find the file " + filename + " in PK3 file " + location.location + ".");
}
else
{
return filedata;
}
}
// This creates a temp file for the speciied file and return the absolute path to the temp file
// NOTE: Callers are responsible for removing the temp file when done!
protected override string CreateTempFile(string filename)
{
// Just copy the file
string tempfile = General.MakeTempFilename(General.Map.TempPath, "wad");
MemoryStream filedata = LoadFile(filename);
File.WriteAllBytes(tempfile, filedata.ToArray());
filedata.Dispose();
return tempfile;
}
// Public version to load a file
internal MemoryStream ExtractFile(string filename)
{
return LoadFile(filename);
}
#endregion
}
}

View file

@ -0,0 +1,413 @@
#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* 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.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using CodeImp.DoomBuilder.IO;
#endregion
namespace CodeImp.DoomBuilder.Data
{
internal abstract class PK3StructuredReader : DataReader
{
#region ================== Constants
private const string PATCHES_DIR = "patches";
private const string TEXTURES_DIR = "textures";
private const string FLATS_DIR = "flats";
private const string HIRES_DIR = "hires";
private const string SPRITES_DIR = "sprites";
#endregion
#region ================== Variables
// Source
private bool roottextures;
private bool rootflats;
// Paths
protected string rootpath;
protected string patchespath;
protected string texturespath;
protected string flatspath;
protected string hirespath;
protected string spritespath;
// WAD files that must be loaded as well
private List<WADReader> wads;
#endregion
#region ================== Properties
#endregion
#region ================== Constructor / Disposer
// Constructor
public PK3StructuredReader(DataLocation dl) : base(dl)
{
// Initialize
this.roottextures = dl.textures;
this.rootflats = dl.flats;
}
// Call this to initialize this class
protected virtual void Initialize(string rootpath)
{
// Initialize
this.rootpath = rootpath;
this.patchespath = Path.Combine(rootpath, PATCHES_DIR);
this.texturespath = Path.Combine(rootpath, TEXTURES_DIR);
this.flatspath = Path.Combine(rootpath, FLATS_DIR);
this.hirespath = Path.Combine(rootpath, HIRES_DIR);
this.spritespath = Path.Combine(rootpath, SPRITES_DIR);
// Load all WAD files in the root as WAD resources
string[] wadfiles = GetFilesWithExt(rootpath, "wad");
wads = new List<WADReader>(wadfiles.Length);
foreach(string w in wadfiles)
{
string tempfile = CreateTempFile(w);
DataLocation wdl = new DataLocation(DataLocation.RESOURCE_WAD, tempfile, false, false);
wads.Add(new WADReader(wdl));
}
}
// Disposer
public override void Dispose()
{
// Not already disposed?
if(!isdisposed)
{
// Clean up
foreach(WADReader wr in wads) wr.Dispose();
// Remove temp files
foreach(WADReader wr in wads)
{
try { File.Delete(wr.Location.location); }
catch(Exception) { }
}
// Done
base.Dispose();
}
}
#endregion
#region ================== Management
// This suspends use of this resource
public override void Suspend()
{
foreach(WADReader wr in wads) wr.Suspend();
base.Suspend();
}
// This resumes use of this resource
public override void Resume()
{
foreach(WADReader wr in wads) wr.Resume();
base.Resume();
}
#endregion
#region ================== Palette
// This loads the PLAYPAL palette
public override Playpal LoadPalette()
{
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Palette from wad(s)
Playpal palette = null;
foreach(WADReader wr in wads)
{
Playpal wadpalette = wr.LoadPalette();
if(wadpalette != null) palette = wadpalette;
}
// Done
return palette;
}
#endregion
#region ================== Textures
// This loads the textures
public override ICollection<ImageData> LoadTextures(PatchNames pnames)
{
List<ImageData> images = new List<ImageData>();
ICollection<ImageData> collection;
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Load from wad files (NOTE: backward order, because the last wad's images have priority)
for(int i = wads.Count - 1; i >= 0; i--)
{
collection = wads[i].LoadTextures(pnames);
AddImagesToList(images, collection);
}
// Should we load the images in this directory as textures?
if(roottextures)
{
collection = LoadDirectoryImages(rootpath, false);
AddImagesToList(images, collection);
}
// TODO: Add support for hires texture here
// Add images from texture directory
collection = LoadDirectoryImages(texturespath, false);
AddImagesToList(images, collection);
// Load TEXTURE1 lump file
List<ImageData> imgset = new List<ImageData>();
string texture1file = FindFirstFile(rootpath, "TEXTURE1");
if((texture1file != null) && FileExists(texture1file))
{
MemoryStream filedata = LoadFile(texture1file);
WADReader.LoadTextureSet(filedata, ref imgset, pnames);
filedata.Dispose();
}
// Load TEXTURE2 lump file
string texture2file = FindFirstFile(rootpath, "TEXTURE2");
if((texture2file != null) && FileExists(texture2file))
{
MemoryStream filedata = LoadFile(texture2file);
WADReader.LoadTextureSet(filedata, ref imgset, pnames);
filedata.Dispose();
}
// Add images from TEXTURE1 and TEXTURE2 lump files
AddImagesToList(images, imgset);
return images;
}
// This returns the patch names from the PNAMES lump
// A directory resource does not support this lump, but the wads in the directory may contain this lump
public override PatchNames LoadPatchNames()
{
PatchNames pnames;
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Load from wad files
// Note the backward order, because the last wad's images have priority
for(int i = wads.Count - 1; i >= 0; i--)
{
pnames = wads[i].LoadPatchNames();
if(pnames != null) return pnames;
}
// If none of the wads provides patch names, let's see if we can
string pnamesfile = FindFirstFile(rootpath, "PNAMES");
if((pnamesfile != null) && FileExists(pnamesfile))
{
MemoryStream pnamesdata = LoadFile(pnamesfile);
pnames = new PatchNames(pnamesdata);
pnamesdata.Dispose();
return pnames;
}
return null;
}
// This finds and returns a patch stream
public override Stream GetPatchData(string pname)
{
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Find in any of the wad files
// Note the backward order, because the last wad's images have priority
for(int i = wads.Count - 1; i >= 0; i--)
{
Stream data = wads[i].GetPatchData(pname);
if(data != null) return data;
}
// Find in patches directory
string filename = FindFirstFile(patchespath, pname);
if((filename != null) && FileExists(filename))
{
return LoadFile(filename);
}
// Nothing found
return null;
}
#endregion
#region ================== Flats
// This loads the textures
public override ICollection<ImageData> LoadFlats()
{
List<ImageData> images = new List<ImageData>();
ICollection<ImageData> collection;
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Load from wad files
// Note the backward order, because the last wad's images have priority
for(int i = wads.Count - 1; i >= 0; i--)
{
collection = wads[i].LoadFlats();
AddImagesToList(images, collection);
}
// Should we load the images in this directory as flats?
if(rootflats)
{
collection = LoadDirectoryImages(rootpath, true);
AddImagesToList(images, collection);
}
// Add images from flats directory
collection = LoadDirectoryImages(flatspath, true);
AddImagesToList(images, collection);
return images;
}
#endregion
#region ================== Sprites
// This finds and returns a sprite stream
public override Stream GetSpriteData(string pname)
{
string pfilename = pname.Replace('\\', '^');
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Find in any of the wad files
for(int i = wads.Count - 1; i >= 0; i--)
{
Stream sprite = wads[i].GetSpriteData(pname);
if(sprite != null) return sprite;
}
// Find in sprites directory
string filename = FindFirstFile(spritespath, pfilename);
if((filename != null) && FileExists(filename))
{
return LoadFile(filename);
}
// Nothing found
return null;
}
#endregion
#region ================== Methods
// This loads the images in this directory
private ICollection<ImageData> LoadDirectoryImages(string path, bool flats)
{
List<ImageData> images = new List<ImageData>();
string[] files;
string name;
// Go for all files
files = GetAllFiles(path);
foreach(string f in files)
{
// Make the texture name from filename without extension
name = Path.GetFileNameWithoutExtension(f).ToUpperInvariant();
if(name.Length > 8) name = name.Substring(0, 8);
// Add image to list
images.Add(CreateImage(name, f, flats));
}
// Return result
return images;
}
// This copies images from a collection unless they already exist in the list
private void AddImagesToList(List<ImageData> targetlist, ICollection<ImageData> sourcelist)
{
// Go for all source images
foreach(ImageData src in sourcelist)
{
// Check if exists in target list
bool alreadyexists = false;
foreach(ImageData tgt in targetlist)
{
if(tgt.LongName == src.LongName)
{
alreadyexists = true;
break;
}
}
// Add source image to target list
if(!alreadyexists) targetlist.Add(src);
}
}
// This must create an image
protected abstract ImageData CreateImage(string name, string filename, bool flat);
// This must return true if the specified file exists
protected abstract bool FileExists(string filename);
// This must return all files in a given directory
protected abstract string[] GetAllFiles(string path);
// This must return all files in a given directory that match the given extension
protected abstract string[] GetFilesWithExt(string path, string extension);
// This must find the first file that has the specific name, regardless of file extension
protected abstract string FindFirstFile(string path, string beginswith);
// This must load an entire file in memory and returns the stream
// NOTE: Callers are responsible for disposing the stream!
protected abstract MemoryStream LoadFile(string filename);
// This must create a temp file for the speciied file and return the absolute path to the temp file
// NOTE: Callers are responsible for removing the temp file when done!
protected abstract string CreateTempFile(string filename);
#endregion
}
}

View file

@ -80,6 +80,7 @@ namespace CodeImp.DoomBuilder.Data
lock(this)
{
// Get the patch data stream
if(bitmap != null) bitmap.Dispose(); bitmap = null;
patchdata = General.Map.Data.GetPatchData(lumpname);
if(patchdata != null)
{
@ -92,16 +93,31 @@ namespace CodeImp.DoomBuilder.Data
// Get a reader for the data
reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette);
if(reader is UnknownImageReader)
if(!(reader is UnknownImageReader))
{
// Data is in an unknown format!
General.WriteLogLine("WARNING: Image lump '" + lumpname + "' data format could not be read, while loading texture '" + this.Name + "'!");
// Load the image
mem.Seek(0, SeekOrigin.Begin);
try { bitmap = reader.ReadAsBitmap(mem); }
catch(InvalidDataException)
{
// Data cannot be read!
bitmap = null;
}
}
// Load the image
mem.Seek(0, SeekOrigin.Begin);
bitmap = reader.ReadAsBitmap(mem);
if(bitmap == null) return;
// Not loaded?
if(bitmap == null)
{
General.WriteLogLine("WARNING: Image lump '" + lumpname + "' data format could not be read, while loading texture '" + this.Name + "'!");
loadfailed = true;
return;
}
// Get width and height from image
width = bitmap.Size.Width;
height = bitmap.Size.Height;
scaledwidth = (float)width * scalex;
scaledheight = (float)height * scaley;
// Done
mem.Dispose();

View file

@ -201,7 +201,7 @@ namespace CodeImp.DoomBuilder.Data
}
// This loads a set of textures
private void LoadTextureSet(Stream texturedata, ref List<ImageData> images, PatchNames pnames)
public static void LoadTextureSet(Stream texturedata, ref List<ImageData> images, PatchNames pnames)
{
BinaryReader reader = new BinaryReader(texturedata);
int flags, width, height, patches, px, py, pi;

View file

@ -176,7 +176,7 @@ namespace CodeImp.DoomBuilder.Editing
break;
case SOURCE_FILE:
backimage = new FileImage(background, background);
backimage = new FileImage(background, background, false, 1.0f, 1.0f);
break;
}

View file

@ -56,7 +56,7 @@ namespace CodeImp.DoomBuilder
internal static extern void ZeroMemory(IntPtr dest, int size);
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
internal static extern unsafe void CopyMemory(void* dst, void* src, UIntPtr length);
internal static extern unsafe void CopyMemory(void* dst, void* src, uint length);
[DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
internal static extern int SendMessage(IntPtr hwnd, uint Msg, int wParam, int lParam);

View file

@ -103,7 +103,7 @@ namespace CodeImp.DoomBuilder.IO
targetdata = (PixelColor*)bitmapdata.Scan0.ToPointer();
// Copy the pixels
General.CopyMemory((void*)targetdata, (void*)pixeldata.Pointer, new UIntPtr((uint)(width * height * sizeof(PixelColor))));
General.CopyMemory(targetdata, pixeldata.Pointer, (uint)(width * height * sizeof(PixelColor)));
// Done
bmp.UnlockBits(bitmapdata);

View file

@ -119,7 +119,7 @@ namespace CodeImp.DoomBuilder.IO
targetdata = (PixelColor*)bitmapdata.Scan0.ToPointer();
// Copy the pixels
General.CopyMemory((void*)targetdata, (void*)pixeldata.Pointer, new UIntPtr((uint)(width * height * sizeof(PixelColor))));
General.CopyMemory(targetdata, pixeldata.Pointer, (uint)(width * height * sizeof(PixelColor)));
// Done
bmp.UnlockBits(bitmapdata);

View file

@ -119,7 +119,7 @@ namespace CodeImp.DoomBuilder.IO
fixed(void* bp = namebytes)
{
General.CopyMemory(&value, bp, new UIntPtr(bytes));
General.CopyMemory(&value, bp, bytes);
}
return value;

View file

@ -132,7 +132,7 @@ namespace CodeImp.DoomBuilder.Windows
// Set this file as background
backgroundname = browsefile.FileName;
backgroundsource = GridSetup.SOURCE_FILE;
ImageData img = new FileImage(backgroundname, backgroundname);
ImageData img = new FileImage(backgroundname, backgroundname, false, 1.0f, 1.0f);
img.LoadImage();
General.DisplayZoomedImage(backgroundimage, new Bitmap(img.Bitmap));
img.Dispose();