TEXTURES support: both patch Blend syntaxes are now supported (previously only "Blend <string color>[,<float alpha>]" was supported).

Added X11R6RGB parser.
Rewritten parts of unhandled exceptions handling to reduce the chance of additional exceptions happening after the first one.
This commit is contained in:
MaxED 2016-03-16 23:26:53 +00:00
parent 1703af1c82
commit adadca9d99
20 changed files with 605 additions and 390 deletions

View file

@ -3372,7 +3372,7 @@ zdoom
{ {
1 = "Disable light effects"; 1 = "Disable light effects";
2 = "Restrict light inside"; 2 = "Restrict light inside";
4 = "Fog effect (GZDoom) / Fade effect (ZDoom)"; 4 = "Fog effect (GZDoom only)";
8 = "Ignore bottom height"; 8 = "Ignore bottom height";
16 = "Use upper texture"; 16 = "Use upper texture";
32 = "Use lower texture"; 32 = "Use lower texture";

View file

@ -1021,6 +1021,7 @@
<Compile Include="ZDoom\TexturesParser.cs" /> <Compile Include="ZDoom\TexturesParser.cs" />
<Compile Include="ZDoom\TextureStructure.cs" /> <Compile Include="ZDoom\TextureStructure.cs" />
<Compile Include="ZDoom\VoxeldefParser.cs" /> <Compile Include="ZDoom\VoxeldefParser.cs" />
<Compile Include="ZDoom\X11R6RGBParser.cs" />
<Compile Include="ZDoom\ZDTextParser.cs" /> <Compile Include="ZDoom\ZDTextParser.cs" />
<Compile Include="ZDoom\DecorateParser.cs" /> <Compile Include="ZDoom\DecorateParser.cs" />
<Compile Include="ZDoom\StateStructure.cs" /> <Compile Include="ZDoom\StateStructure.cs" />

View file

@ -42,6 +42,7 @@ namespace CodeImp.DoomBuilder.Config
ANIMDEFS = 9, ANIMDEFS = 9,
REVERBS = 10, REVERBS = 10,
TERRAIN = 11, TERRAIN = 11,
X11R6RGB = 12,
} }
internal class ScriptConfiguration : IComparable<ScriptConfiguration> internal class ScriptConfiguration : IComparable<ScriptConfiguration>

View file

@ -97,6 +97,7 @@ namespace CodeImp.DoomBuilder.Data
private string[] soundsequences; private string[] soundsequences;
private string[] terrainnames; private string[] terrainnames;
private string[] damagetypes; private string[] damagetypes;
private Dictionary<string, PixelColor> knowncolors; // Colors parsed from X11R6RGB lump. Color names are lowercase without spaces
//mxd. Text resources //mxd. Text resources
private Dictionary<ScriptType, HashSet<TextResource>> textresources; private Dictionary<ScriptType, HashSet<TextResource>> textresources;
@ -156,6 +157,7 @@ namespace CodeImp.DoomBuilder.Data
public string[] SoundSequences { get { return soundsequences; } } public string[] SoundSequences { get { return soundsequences; } }
public string[] TerrainNames { get { return terrainnames; } } public string[] TerrainNames { get { return terrainnames; } }
public string[] DamageTypes { get { return damagetypes; } } public string[] DamageTypes { get { return damagetypes; } }
public Dictionary<string, PixelColor> KnownColors { get { return knowncolors; } }
internal Dictionary<ScriptType, HashSet<TextResource>> TextResources { get { return textresources; } } internal Dictionary<ScriptType, HashSet<TextResource>> TextResources { get { return textresources; } }
//mxd //mxd
@ -326,6 +328,7 @@ namespace CodeImp.DoomBuilder.Data
terrainnames = new string[0]; terrainnames = new string[0];
textresources = new Dictionary<ScriptType, HashSet<TextResource>>(); textresources = new Dictionary<ScriptType, HashSet<TextResource>>();
damagetypes = new string[0]; damagetypes = new string[0];
knowncolors = new Dictionary<string, PixelColor>(StringComparer.OrdinalIgnoreCase);
// Load texture sets // Load texture sets
foreach(DefinedTextureSet ts in General.Map.ConfigSettings.TextureSets) foreach(DefinedTextureSet ts in General.Map.ConfigSettings.TextureSets)
@ -386,6 +389,7 @@ namespace CodeImp.DoomBuilder.Data
} }
// Load stuff // Load stuff
LoadX11R6RGB(); //mxd
LoadPalette(); LoadPalette();
Dictionary<string, TexturesParser> cachedparsers = new Dictionary<string, TexturesParser>(); //mxd Dictionary<string, TexturesParser> cachedparsers = new Dictionary<string, TexturesParser>(); //mxd
int texcount = LoadTextures(texturesonly, texturenamesshorttofull, cachedparsers); int texcount = LoadTextures(texturesonly, texturenamesshorttofull, cachedparsers);
@ -588,6 +592,7 @@ namespace CodeImp.DoomBuilder.Data
terrainnames = null; //mxd terrainnames = null; //mxd
textresources = null; //mxd textresources = null; //mxd
damagetypes = null; //mxd damagetypes = null; //mxd
knowncolors = null; //mxd
texturenames = null; texturenames = null;
flatnames = null; flatnames = null;
imageque = null; imageque = null;
@ -801,10 +806,7 @@ namespace CodeImp.DoomBuilder.Data
} }
while(true); while(true);
} }
catch(ThreadInterruptedException) catch(ThreadInterruptedException) { }
{
return;
}
} }
// This adds an image for background loading or unloading // This adds an image for background loading or unloading
@ -2460,6 +2462,34 @@ namespace CodeImp.DoomBuilder.Data
terrainnames = names.ToArray(); terrainnames = names.ToArray();
} }
//mxd. This loads X11R6RGB
private void LoadX11R6RGB()
{
X11R6RGBParser parser = new X11R6RGBParser();
foreach(DataReader dr in containers)
{
currentreader = dr;
IEnumerable<TextResourceData> streams = dr.GetX11R6RGBData();
// Parse the data
foreach(TextResourceData data in streams)
{
parser.Parse(data, true);
// Report errors?
if(parser.HasError) parser.LogError();
}
}
// Add to text resources collection
textresources[parser.ScriptType] = new HashSet<TextResource>(parser.TextResources.Values);
currentreader = null;
// Set as collection
knowncolors = parser.KnownColors;
}
//mxd //mxd
internal TextResourceData GetTextResourceData(string name) internal TextResourceData GetTextResourceData(string name)
{ {

View file

@ -240,6 +240,9 @@ namespace CodeImp.DoomBuilder.Data
//mxd. When implemented, this returns the TERRAIN lump //mxd. When implemented, this returns the TERRAIN lump
public abstract IEnumerable<TextResourceData> GetTerrainData(); public abstract IEnumerable<TextResourceData> GetTerrainData();
//mxd. When implemented, this returns the X11R6RGB lump
public abstract IEnumerable<TextResourceData> GetX11R6RGBData();
//mxd. When implemented, this returns the list of voxel model names //mxd. When implemented, this returns the list of voxel model names
public abstract IEnumerable<string> GetVoxelNames(); public abstract IEnumerable<string> GetVoxelNames();

View file

@ -701,7 +701,6 @@ namespace CodeImp.DoomBuilder.Data
#region ================== TERRAIN (mxd) #region ================== TERRAIN (mxd)
//mxd
public override IEnumerable<TextResourceData> GetTerrainData() public override IEnumerable<TextResourceData> GetTerrainData()
{ {
// Error when suspended // Error when suspended
@ -722,6 +721,28 @@ namespace CodeImp.DoomBuilder.Data
#endregion #endregion
#region ================== XBRSBSBB11 (mxd)
public override IEnumerable<TextResourceData> GetX11R6RGBData()
{
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
List<TextResourceData> result = new List<TextResourceData>();
string[] files = GetAllFilesWithTitle("", "X11R6RGB", false);
// Add to collection
foreach(string s in files)
result.Add(new TextResourceData(this, LoadFile(s), s, true));
// Find in any of the wad files
foreach(WADReader wr in wads) result.AddRange(wr.GetTerrainData());
return result;
}
#endregion
#region ================== Methods #region ================== Methods
// This loads the images in this directory // This loads the images in this directory

View file

@ -98,7 +98,7 @@ namespace CodeImp.DoomBuilder.Data
{ {
// Add it // Add it
patches.Add(patch); patches.Add(patch);
if(patch.lumpname == Name) hasPatchWithSameName = true; //mxd if(patch.LumpName == Name) hasPatchWithSameName = true; //mxd
} }
// This loads the image // This loads the image
@ -143,11 +143,11 @@ namespace CodeImp.DoomBuilder.Data
foreach(TexturePatch p in patches) foreach(TexturePatch p in patches)
{ {
//mxd. Some patches (like "TNT1A0") should be skipped //mxd. Some patches (like "TNT1A0") should be skipped
if(p.skip) continue; if(p.Skip) continue;
// Get the patch data stream // Get the patch data stream
string patchlocation = string.Empty; //mxd string patchlocation = string.Empty; //mxd
Stream patchdata = General.Map.Data.GetPatchData(p.lumpname, p.haslongname, ref patchlocation); Stream patchdata = General.Map.Data.GetPatchData(p.LumpName, p.HasLongName, ref patchlocation);
if(patchdata != null) if(patchdata != null)
{ {
// Copy patch data to memory // Copy patch data to memory
@ -170,7 +170,7 @@ namespace CodeImp.DoomBuilder.Data
if(reader is UnknownImageReader) if(reader is UnknownImageReader)
{ {
// Data is in an unknown format! // Data is in an unknown format!
General.ErrorLogger.Add(ErrorType.Error, "Patch lump \"" + Path.Combine(patchlocation, p.lumpname) + "\" data format could not be read, while loading texture \"" + this.Name + "\""); General.ErrorLogger.Add(ErrorType.Error, "Patch lump \"" + Path.Combine(patchlocation, p.LumpName) + "\" data format could not be read, while loading texture \"" + this.Name + "\"");
missingpatches++; //mxd missingpatches++; //mxd
} }
} }
@ -184,7 +184,7 @@ namespace CodeImp.DoomBuilder.Data
catch(InvalidDataException) catch(InvalidDataException)
{ {
// Data cannot be read! // Data cannot be read!
General.ErrorLogger.Add(ErrorType.Error, "Patch lump \"" + p.lumpname + "\" data format could not be read, while loading texture \"" + this.Name + "\""); General.ErrorLogger.Add(ErrorType.Error, "Patch lump \"" + p.LumpName + "\" data format could not be read, while loading texture \"" + this.Name + "\"");
missingpatches++; //mxd missingpatches++; //mxd
} }
@ -194,7 +194,7 @@ namespace CodeImp.DoomBuilder.Data
patchbmp = TransformPatch(p, patchbmp); patchbmp = TransformPatch(p, patchbmp);
// Draw the patch on the texture image // Draw the patch on the texture image
Rectangle tgtrect = new Rectangle(p.x, p.y, patchbmp.Size.Width, patchbmp.Size.Height); Rectangle tgtrect = new Rectangle(p.X, p.Y, patchbmp.Size.Width, patchbmp.Size.Height);
g.DrawImageUnscaledAndClipped(patchbmp, tgtrect); g.DrawImageUnscaledAndClipped(patchbmp, tgtrect);
patchbmp.Dispose(); patchbmp.Dispose();
} }
@ -208,7 +208,7 @@ namespace CodeImp.DoomBuilder.Data
//mxd. ZDoom can use any known graphic as patch //mxd. ZDoom can use any known graphic as patch
if(General.Map.Config.MixTexturesFlats) if(General.Map.Config.MixTexturesFlats)
{ {
ImageData img = General.Map.Data.GetTextureImage(p.lumpname); ImageData img = General.Map.Data.GetTextureImage(p.LumpName);
if(!(img is UnknownImage) && img != this) if(!(img is UnknownImage) && img != this)
{ {
if(!img.IsImageLoaded) img.LoadImage(); if(!img.IsImageLoaded) img.LoadImage();
@ -217,7 +217,7 @@ namespace CodeImp.DoomBuilder.Data
Bitmap patchbmp = TransformPatch(p, new Bitmap(img.GetBitmap())); Bitmap patchbmp = TransformPatch(p, new Bitmap(img.GetBitmap()));
// Draw the patch on the texture image // Draw the patch on the texture image
Rectangle tgtrect = new Rectangle(p.x, p.y, patchbmp.Size.Width, patchbmp.Size.Height); Rectangle tgtrect = new Rectangle(p.X, p.Y, patchbmp.Size.Width, patchbmp.Size.Height);
g.DrawImageUnscaledAndClipped(patchbmp, tgtrect); g.DrawImageUnscaledAndClipped(patchbmp, tgtrect);
patchbmp.Dispose(); patchbmp.Dispose();
@ -226,7 +226,7 @@ namespace CodeImp.DoomBuilder.Data
} }
// Missing a patch lump! // Missing a patch lump!
General.ErrorLogger.Add(ErrorType.Error, "Missing patch lump \"" + p.lumpname + "\" while loading texture \"" + this.Name + "\""); General.ErrorLogger.Add(ErrorType.Error, "Missing patch lump \"" + p.LumpName + "\" while loading texture \"" + this.Name + "\"");
missingpatches++; //mxd missingpatches++; //mxd
} }
} }
@ -249,20 +249,20 @@ namespace CodeImp.DoomBuilder.Data
private Bitmap TransformPatch(TexturePatch p, Bitmap patchbmp) private Bitmap TransformPatch(TexturePatch p, Bitmap patchbmp)
{ {
//mxd. Flip //mxd. Flip
if(p.flipx || p.flipy) if(p.FlipX || p.FlipY)
{ {
RotateFlipType flip; RotateFlipType flip;
if(p.flipx && !p.flipy) flip = RotateFlipType.RotateNoneFlipX; if(p.FlipX && !p.FlipY) flip = RotateFlipType.RotateNoneFlipX;
else if(!p.flipx && p.flipy) flip = RotateFlipType.RotateNoneFlipY; else if(!p.FlipX && p.FlipY) flip = RotateFlipType.RotateNoneFlipY;
else flip = RotateFlipType.RotateNoneFlipXY; else flip = RotateFlipType.RotateNoneFlipXY;
patchbmp.RotateFlip(flip); patchbmp.RotateFlip(flip);
} }
//mxd. Then rotate. I do it this way because RotateFlip function rotates THEN flips, and GZDoom does it the other way around. //mxd. Then rotate. I do it this way because RotateFlip function rotates THEN flips, and GZDoom does it the other way around.
if(p.rotate != 0) if(p.Rotate != 0)
{ {
RotateFlipType rotate; RotateFlipType rotate;
switch(p.rotate) switch(p.Rotate)
{ {
case 90: rotate = RotateFlipType.Rotate90FlipNone; break; case 90: rotate = RotateFlipType.Rotate90FlipNone; break;
case 180: rotate = RotateFlipType.Rotate180FlipNone; break; case 180: rotate = RotateFlipType.Rotate180FlipNone; break;
@ -272,7 +272,7 @@ namespace CodeImp.DoomBuilder.Data
} }
// Adjust patch alpha, apply tint or blend // Adjust patch alpha, apply tint or blend
if(p.blendstyle != TexturePathBlendStyle.None || p.style != TexturePathRenderStyle.Copy) if(p.BlendStyle != TexturePathBlendStyle.NONE || p.RenderStyle != TexturePathRenderStyle.COPY)
{ {
BitmapData bmpdata = null; BitmapData bmpdata = null;
@ -282,61 +282,61 @@ namespace CodeImp.DoomBuilder.Data
} }
catch(Exception e) catch(Exception e)
{ {
General.ErrorLogger.Add(ErrorType.Error, "Cannot lock image \"" + p.lumpname + "\" for alpha adjustment. " + e.GetType().Name + ": " + e.Message); General.ErrorLogger.Add(ErrorType.Error, "Cannot lock image \"" + p.LumpName + "\" for alpha adjustment. " + e.GetType().Name + ": " + e.Message);
} }
if(bmpdata != null) if(bmpdata != null)
{ {
PixelColor* pixels = (PixelColor*)(bmpdata.Scan0.ToPointer()); PixelColor* pixels = (PixelColor*)(bmpdata.Scan0.ToPointer());
int numpixels = bmpdata.Width * bmpdata.Height; int numpixels = bmpdata.Width * bmpdata.Height;
int patchalpha = (int)Math.Round(General.Clamp(p.alpha, 0f, 1f) * 255); //convert alpha to [0-255] range int patchalpha = (int)Math.Round(General.Clamp(p.Alpha, 0f, 1f) * 255); //convert alpha to [0-255] range
//mxd. Blend/Tint support //mxd. Blend/Tint support
if(p.blendstyle == TexturePathBlendStyle.Blend) if(p.BlendStyle == TexturePathBlendStyle.BLEND)
{ {
for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
{ {
cp->r = (byte)((cp->r * p.blend.r) * PixelColor.BYTE_TO_FLOAT); cp->r = (byte)((cp->r * p.BlendColor.r) * PixelColor.BYTE_TO_FLOAT);
cp->g = (byte)((cp->g * p.blend.g) * PixelColor.BYTE_TO_FLOAT); cp->g = (byte)((cp->g * p.BlendColor.g) * PixelColor.BYTE_TO_FLOAT);
cp->b = (byte)((cp->b * p.blend.b) * PixelColor.BYTE_TO_FLOAT); cp->b = (byte)((cp->b * p.BlendColor.b) * PixelColor.BYTE_TO_FLOAT);
} }
} }
else if(p.blendstyle == TexturePathBlendStyle.Tint) else if(p.BlendStyle == TexturePathBlendStyle.TINT)
{ {
float tintammount = p.tintammount - 0.1f; float tintammount = p.BlendColor.a * PixelColor.BYTE_TO_FLOAT;// -0.1f;
if(tintammount > 0) if(tintammount > 0)
{ {
float br = p.blend.r * PixelColor.BYTE_TO_FLOAT * tintammount; float br = p.BlendColor.r * PixelColor.BYTE_TO_FLOAT * tintammount;
float bg = p.blend.g * PixelColor.BYTE_TO_FLOAT * tintammount; float bg = p.BlendColor.g * PixelColor.BYTE_TO_FLOAT * tintammount;
float bb = p.blend.b * PixelColor.BYTE_TO_FLOAT * tintammount; float bb = p.BlendColor.b * PixelColor.BYTE_TO_FLOAT * tintammount;
float invTint = 1.0f - tintammount; float invtintammount = 1.0f - tintammount;
for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
{ {
cp->r = (byte)(((cp->r * PixelColor.BYTE_TO_FLOAT) * invTint + br) * 255.0f); cp->r = (byte)(((cp->r * PixelColor.BYTE_TO_FLOAT) * invtintammount + br) * 255.0f);
cp->g = (byte)(((cp->g * PixelColor.BYTE_TO_FLOAT) * invTint + bg) * 255.0f); cp->g = (byte)(((cp->g * PixelColor.BYTE_TO_FLOAT) * invtintammount + bg) * 255.0f);
cp->b = (byte)(((cp->b * PixelColor.BYTE_TO_FLOAT) * invTint + bb) * 255.0f); cp->b = (byte)(((cp->b * PixelColor.BYTE_TO_FLOAT) * invtintammount + bb) * 255.0f);
} }
} }
} }
//mxd. Apply RenderStyle //mxd. Apply RenderStyle
if(p.style == TexturePathRenderStyle.Blend) if(p.RenderStyle == TexturePathRenderStyle.BLEND)
{ {
for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT); cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT);
} }
//mxd. We need a copy of underlying part of texture for these styles //mxd. We need a copy of underlying part of texture for these styles
else if(p.style != TexturePathRenderStyle.Copy) else if(p.RenderStyle != TexturePathRenderStyle.COPY)
{ {
// Copy portion of texture // Copy portion of texture
int lockWidth = (p.x + patchbmp.Size.Width > bitmap.Width) ? bitmap.Width - p.x : patchbmp.Size.Width; int lockWidth = (p.X + patchbmp.Size.Width > bitmap.Width) ? bitmap.Width - p.X : patchbmp.Size.Width;
int lockHeight = (p.y + patchbmp.Size.Height > bitmap.Height) ? bitmap.Height - p.y : patchbmp.Size.Height; int lockHeight = (p.Y + patchbmp.Size.Height > bitmap.Height) ? bitmap.Height - p.Y : patchbmp.Size.Height;
Bitmap source = new Bitmap(patchbmp.Size.Width, patchbmp.Size.Height); Bitmap source = new Bitmap(patchbmp.Size.Width, patchbmp.Size.Height);
using(Graphics sg = Graphics.FromImage(source)) using(Graphics sg = Graphics.FromImage(source))
sg.DrawImageUnscaled(bitmap, new Rectangle(-p.x, -p.y, lockWidth, lockHeight)); sg.DrawImageUnscaled(bitmap, new Rectangle(-p.X, -p.Y, lockWidth, lockHeight));
// Lock texture // Lock texture
BitmapData texturebmpdata = null; BitmapData texturebmpdata = null;
@ -355,9 +355,9 @@ namespace CodeImp.DoomBuilder.Data
PixelColor* texturepixels = (PixelColor*)(texturebmpdata.Scan0.ToPointer()); PixelColor* texturepixels = (PixelColor*)(texturebmpdata.Scan0.ToPointer());
PixelColor* tcp = texturepixels + numpixels - 1; PixelColor* tcp = texturepixels + numpixels - 1;
switch(p.style) switch(p.RenderStyle)
{ {
case TexturePathRenderStyle.Add: case TexturePathRenderStyle.ADD:
for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
{ {
cp->r = (byte)Math.Min(255, cp->r + tcp->r); cp->r = (byte)Math.Min(255, cp->r + tcp->r);
@ -368,7 +368,7 @@ namespace CodeImp.DoomBuilder.Data
} }
break; break;
case TexturePathRenderStyle.Subtract: case TexturePathRenderStyle.SUBTRACT:
for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
{ {
cp->r = (byte)Math.Max(0, tcp->r - cp->r); cp->r = (byte)Math.Max(0, tcp->r - cp->r);
@ -379,7 +379,7 @@ namespace CodeImp.DoomBuilder.Data
} }
break; break;
case TexturePathRenderStyle.ReverseSubtract: case TexturePathRenderStyle.REVERSE_SUBTRACT:
for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
{ {
cp->r = (byte)Math.Max(0, cp->r - tcp->r); cp->r = (byte)Math.Max(0, cp->r - tcp->r);
@ -390,7 +390,7 @@ namespace CodeImp.DoomBuilder.Data
} }
break; break;
case TexturePathRenderStyle.Modulate: case TexturePathRenderStyle.MODULATE:
for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
{ {
cp->r = (byte)((cp->r * tcp->r) * PixelColor.BYTE_TO_FLOAT); cp->r = (byte)((cp->r * tcp->r) * PixelColor.BYTE_TO_FLOAT);

View file

@ -64,7 +64,7 @@ namespace CodeImp.DoomBuilder.Data
// Add it // Add it
patches.Add(patch); patches.Add(patch);
if(patch.lumpname == Name) hasPatchWithSameName = true; //mxd if(patch.LumpName == Name) hasPatchWithSameName = true; //mxd
} }
// This loads the image // This loads the image
@ -103,7 +103,7 @@ namespace CodeImp.DoomBuilder.Data
{ {
// Get the patch data stream // Get the patch data stream
string patchlocation = string.Empty; //mxd string patchlocation = string.Empty; //mxd
Stream patchdata = General.Map.Data.GetPatchData(p.lumpname, p.haslongname, ref patchlocation); Stream patchdata = General.Map.Data.GetPatchData(p.LumpName, p.HasLongName, ref patchlocation);
if(patchdata != null) if(patchdata != null)
{ {
// Copy patch data to memory // Copy patch data to memory
@ -125,7 +125,7 @@ namespace CodeImp.DoomBuilder.Data
if(reader is UnknownImageReader) if(reader is UnknownImageReader)
{ {
// Data is in an unknown format! // Data is in an unknown format!
General.ErrorLogger.Add(ErrorType.Error, "Patch lump \"" + Path.Combine(patchlocation, p.lumpname) + "\" data format could not be read, while loading texture \"" + this.Name + "\". Does this lump contain valid picture data at all?"); General.ErrorLogger.Add(ErrorType.Error, "Patch lump \"" + Path.Combine(patchlocation, p.LumpName) + "\" data format could not be read, while loading texture \"" + this.Name + "\". Does this lump contain valid picture data at all?");
loadfailed = true; loadfailed = true;
missingpatches++; //mxd missingpatches++; //mxd
} }
@ -135,11 +135,11 @@ namespace CodeImp.DoomBuilder.Data
{ {
// Draw the patch // Draw the patch
mem.Seek(0, SeekOrigin.Begin); mem.Seek(0, SeekOrigin.Begin);
try { reader.DrawToPixelData(mem, pixels, width, height, p.x, p.y); } try { reader.DrawToPixelData(mem, pixels, width, height, p.X, p.Y); }
catch(InvalidDataException) catch(InvalidDataException)
{ {
// Data cannot be read! // Data cannot be read!
General.ErrorLogger.Add(ErrorType.Error, "Patch lump \"" + p.lumpname + "\" data format could not be read, while loading texture \"" + this.Name + "\". Does this lump contain valid picture data at all?"); General.ErrorLogger.Add(ErrorType.Error, "Patch lump \"" + p.LumpName + "\" data format could not be read, while loading texture \"" + this.Name + "\". Does this lump contain valid picture data at all?");
loadfailed = true; loadfailed = true;
missingpatches++; //mxd missingpatches++; //mxd
} }
@ -151,7 +151,7 @@ namespace CodeImp.DoomBuilder.Data
else else
{ {
// Missing a patch lump! // Missing a patch lump!
General.ErrorLogger.Add(ErrorType.Error, "Missing patch lump \"" + p.lumpname + "\" while loading texture \"" + this.Name + "\". Did you forget to include required resources?"); General.ErrorLogger.Add(ErrorType.Error, "Missing patch lump \"" + p.LumpName + "\" while loading texture \"" + this.Name + "\". Did you forget to include required resources?");
loadfailed = true; loadfailed = true;
missingpatches++; //mxd missingpatches++; //mxd
} }

View file

@ -26,93 +26,90 @@ namespace CodeImp.DoomBuilder.Data
{ {
public enum TexturePathRenderStyle public enum TexturePathRenderStyle
{ {
Copy, COPY,
Blend, BLEND,
Add, ADD,
Subtract, SUBTRACT,
ReverseSubtract, REVERSE_SUBTRACT,
Modulate, MODULATE,
CopyAlpha, COPY_ALPHA,
CopyNewAlpha, //mxd COPY_NEW_ALPHA, //mxd
Overlay, //mxd OVERLAY, //mxd
} }
public enum TexturePathBlendStyle //mxd public enum TexturePathBlendStyle //mxd
{ {
None, NONE,
Blend, BLEND,
Tint TINT
} }
internal struct TexturePatch internal struct TexturePatch
{ {
public readonly string lumpname; public readonly string LumpName;
public readonly int x; public readonly int X;
public readonly int y; public readonly int Y;
public readonly bool flipx; public readonly bool FlipX;
public readonly bool flipy; public readonly bool FlipY;
public readonly bool haslongname; //mxd public readonly bool HasLongName; //mxd
public readonly int rotate; public readonly int Rotate;
public PixelColor blend; public PixelColor BlendColor;
public readonly float alpha; public readonly float Alpha;
public readonly TexturePathRenderStyle style; public readonly TexturePathRenderStyle RenderStyle;
public readonly TexturePathBlendStyle blendstyle; //mxd public readonly TexturePathBlendStyle BlendStyle; //mxd
public readonly float tintammount;//mxd public readonly bool Skip; //mxd
public readonly bool skip; //mxd
// Constructor for simple patches // Constructor for simple patches
public TexturePatch(string lumpname, int x, int y) public TexturePatch(string lumpname, int x, int y)
{ {
// Initialize // Initialize
this.lumpname = lumpname; this.LumpName = lumpname;
this.x = x; this.X = x;
this.y = y; this.Y = y;
this.flipx = false; this.FlipX = false;
this.flipy = false; this.FlipY = false;
this.rotate = 0; this.Rotate = 0;
this.blend = new PixelColor(0, 0, 0, 0); this.BlendColor = new PixelColor(0, 0, 0, 0);
this.alpha = 1.0f; this.Alpha = 1.0f;
this.style = TexturePathRenderStyle.Copy; this.RenderStyle = TexturePathRenderStyle.COPY;
this.blendstyle = TexturePathBlendStyle.None;//mxd this.BlendStyle = TexturePathBlendStyle.NONE;//mxd
this.tintammount = 0; //mxd this.HasLongName = false; //mxd
this.haslongname = false; //mxd this.Skip = false; //mxd
this.skip = false; //mxd
} }
//mxd. Constructor for hires patches //mxd. Constructor for hires patches
public TexturePatch(PatchStructure patch) public TexturePatch(PatchStructure patch)
{ {
// Initialize // Initialize
this.lumpname = patch.Name.ToUpperInvariant(); this.LumpName = patch.Name.ToUpperInvariant();
this.x = patch.OffsetX; this.X = patch.OffsetX;
this.y = patch.OffsetY; this.Y = patch.OffsetY;
this.flipx = patch.FlipX; this.FlipX = patch.FlipX;
this.flipy = patch.FlipY; this.FlipY = patch.FlipY;
this.rotate = patch.Rotation; this.Rotate = patch.Rotation;
this.blend = patch.BlendColor; this.BlendColor = patch.BlendColor;
this.alpha = patch.Alpha; this.Alpha = patch.Alpha;
this.style = patch.RenderStyle; this.RenderStyle = patch.RenderStyle;
this.blendstyle = patch.BlendStyle; this.BlendStyle = patch.BlendStyle;
this.tintammount = patch.TintAmmount; this.HasLongName = (Path.GetFileNameWithoutExtension(this.LumpName) != this.LumpName);
this.haslongname = (Path.GetFileNameWithoutExtension(this.lumpname) != this.lumpname); this.Skip = patch.Skip;
this.skip = patch.Skip;
//mxd. Check data so we don't perform unneeded operations later on //mxd. Check data so we don't perform unneeded operations later on
if(this.alpha == 1.0f) if(this.Alpha == 1.0f)
{ {
switch(this.style) switch(this.RenderStyle)
{ {
case TexturePathRenderStyle.Blend: case TexturePathRenderStyle.BLEND:
case TexturePathRenderStyle.CopyAlpha: case TexturePathRenderStyle.COPY_ALPHA:
case TexturePathRenderStyle.CopyNewAlpha: case TexturePathRenderStyle.COPY_NEW_ALPHA:
case TexturePathRenderStyle.Overlay: case TexturePathRenderStyle.OVERLAY:
this.style = TexturePathRenderStyle.Copy; this.RenderStyle = TexturePathRenderStyle.COPY;
break; break;
} }
} }
//mxd. and get rid of render styles we don't support //mxd. and get rid of render styles we don't support
if(this.style == TexturePathRenderStyle.Overlay) this.style = TexturePathRenderStyle.Copy; if(this.RenderStyle == TexturePathRenderStyle.OVERLAY) this.RenderStyle = TexturePathRenderStyle.COPY;
} }
} }
} }

View file

@ -1015,6 +1015,13 @@ namespace CodeImp.DoomBuilder.Data
return GetAllLumps("TERRAIN"); return GetAllLumps("TERRAIN");
} }
//mxd
public override IEnumerable<TextResourceData> GetX11R6RGBData()
{
if(issuspended) throw new Exception("Data reader is suspended");
return GetAllLumps("X11R6RGB");
}
//mxd //mxd
private IEnumerable<TextResourceData> GetFirstLump(string name) private IEnumerable<TextResourceData> GetFirstLump(string name)
{ {

View file

@ -2,12 +2,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data; using CodeImp.DoomBuilder.Data;
using SlimDX; using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.ZDoom; using CodeImp.DoomBuilder.ZDoom;
using CodeImp.DoomBuilder.GZBuilder.Data; using CodeImp.DoomBuilder.GZBuilder.Data;
@ -607,16 +606,15 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
ReportError("Expected " + fadetype + " color value"); ReportError("Expected " + fadetype + " color value");
return false; return false;
} }
Color4 color = new Color4();
// Try to get the color... // Try to get the color...
if(GetColor(colorval, ref color)) PixelColor color = new PixelColor();
if(GetColorFromString(colorval, ref color))
{ {
if(fadetype == "fade") if(fadetype == "fade")
mapinfo.FadeColor = color; mapinfo.FadeColor = color.ToColorValue();
else else
mapinfo.OutsideFogColor = color; mapinfo.OutsideFogColor = color.ToColorValue();
} }
else //...or not else //...or not
{ {
@ -687,30 +685,5 @@ namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
#endregion #endregion
#region ================== Methods
private static bool GetColor(string name, ref Color4 color)
{
if(name == "black") return true;
//probably it's a hex color (like FFCC11)?
int ci;
if(int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ci))
{
color = new Color4(ci) {Alpha = 1.0f};
return true;
}
//probably it's a color name?
Color c = Color.FromName(name); //should be similar to C++ color name detection, I suppose
if(c.IsKnownColor)
{
color = new Color4(c);
return true;
}
return false;
}
#endregion
} }
} }

View file

@ -12,34 +12,34 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
{ {
public partial class ExceptionDialog : Form public partial class ExceptionDialog : Form
{ {
private readonly bool cannotContinue; private readonly bool isterminating;
private readonly string logPath; private readonly string logpath;
public ExceptionDialog(UnhandledExceptionEventArgs e) public ExceptionDialog(UnhandledExceptionEventArgs e)
{ {
InitializeComponent(); InitializeComponent();
logPath = Path.Combine(General.SettingsPath, @"GZCrash.txt"); logpath = Path.Combine(General.SettingsPath, @"GZCrash.txt");
Exception ex = (Exception)e.ExceptionObject; Exception ex = (Exception)e.ExceptionObject;
errorDescription.Text = "Error in " + ex.Source + ":"; errorDescription.Text = "Error in " + ex.Source + ":";
string sysinfo = GetSystemInfo(); string sysinfo = GetSystemInfo();
using(StreamWriter sw = File.CreateText(logPath)) using(StreamWriter sw = File.CreateText(logpath))
{ {
sw.Write(sysinfo + GetExceptionDescription(ex)); sw.Write(sysinfo + GetExceptionDescription(ex));
} }
errorMessage.Text = ex.Message + Environment.NewLine + ex.StackTrace; errorMessage.Text = ex.Message + Environment.NewLine + ex.StackTrace;
cannotContinue = true; //cannot recover from this... isterminating = e.IsTerminating; // Recoverable?
} }
public ExceptionDialog(ThreadExceptionEventArgs e) public ExceptionDialog(ThreadExceptionEventArgs e)
{ {
InitializeComponent(); InitializeComponent();
logPath = Path.Combine(General.SettingsPath, @"GZCrash.txt"); logpath = Path.Combine(General.SettingsPath, @"GZCrash.txt");
errorDescription.Text = "Error in " + e.Exception.Source + ":"; errorDescription.Text = "Error in " + e.Exception.Source + ":";
string sysinfo = GetSystemInfo(); string sysinfo = GetSystemInfo();
using(StreamWriter sw = File.CreateText(logPath)) using(StreamWriter sw = File.CreateText(logpath))
{ {
sw.Write(sysinfo + GetExceptionDescription(e.Exception)); sw.Write(sysinfo + GetExceptionDescription(e.Exception));
} }
@ -50,78 +50,92 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
public void Setup() public void Setup()
{ {
bContinue.Enabled = !cannotContinue; string[] titles =
{
"0x000000 at 0xFFFFFF. That's probaby bad",
"Here we go again...",
"Uh oh, you're screwed",
"All is lost!",
"Achievement unlocked: CRASH TIME!",
"OH NOES! TEH ERROR!",
"0001000001111011000000000011001101011120110111",
"Nuclear launch detected!",
"Don't send this to Microsoft",
"You. Shall. Not. Pass!!!",
"Yep, we have bugs",
"It's dangerous to go alone. Take this!",
"The operation completed successfully",
"Security Alert Moving cursor is not as safe as you thought",
"Random error appears from north",
"ERROR: NO_ERROR",
"Epic fail",
"At least it's not BSoD...",
"User Error. Please Replace User",
"Brought to you by MaxED!",
"GZDoom Builder proudly presents:",
"You aren't expected to understand this",
"Back to the drawing board...",
"I'm sorry... :(",
"This is a horrbble day for you, and of course, the world",
"Abort, Retry, Fail?",
"You are making progress. I'm afraid that's something I can't allow to happen",
"You are making progress. That's not OK",
"No errors found, restarting computer",
"Does Not Compute!",
"Im sorry, Dave, Im afraid I cant do that",
"What's that? Chicken?",
"It can only be attributable to human error",
"It's now safe to turn off your computer",
"I've got a bad feeling about this",
"YOU CANT DO THAT!",
"Man the Lifeboats! Women and children first!",
"IMPOSSIBURU!!!",
"Now deleting all files. Goodbye",
"General Failure",
"Invalid Error",
"Beam me up Scotty, theres no life out here",
"Well, you ran into something and the game is over",
"I'm good at writing bad code",
"$FUNNY_ERROR_CAPTION",
"In Soviet Russia, exception throws YOU!",
"...and then GZDB was the demons!",
"B U S T E D",
"Freeze mode enabled",
"You feel strange...",
"That doesn't seem to work",
"This function is only available in the retail version of GZDoom Builder",
"You picked up the Random Exception.",
"Pinky says that you're the new hope. Bear that in mind.",
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"Deal with it",
"Error 47",
"YOU DIED",
"Thanks, Obama",
"The God Of Exceptions Demands MORE Exceptions!",
"Good. It's boring here anyway.",
"Shameful display!",
"It's CRASHENING!",
"W-W-W-WIPEOUT!",
"EVERYTHING IS LOST!",
"Your empty is full!",
"Let's see how far this infinite loop goes...",
"Windows 10 is here! RUN!",
"You really screwed up this time!",
"[WFDS]",
"[No]",
"An error has occurred while creating an error",
"Catastrophic failure",
"This time, its the humans fault",
"No error occurred",
"Hey! It looks like you're having an error!",
"What, what, what, what, what, what, what, what, what, what?",
"WARNING: PROGRAMMING BUG IN GZDB!",
"Something happened",
"The Device is Error",
};
string[] titles = {
"0x000000 at 0xFFFFFF. That's probaby bad",
"Here we go again...",
"Uh oh, you're screwed",
"All is lost!",
"Achievement unlocked: CRASH TIME!",
"OH NOES! TEH ERROR!",
"0001000001111011000000000011001101011110110111",
"Nuclear launch detected!",
"Don't send this to Microsoft",
"You. Shall. Not. Pass!!!",
"Yep, we have bugs",
"It's dangerous to go alone. Take this!",
"The operation completed successfully",
"Security Alert Moving cursor is not as safe as you thought",
"Random error appears from north",
"ERROR: NO_ERROR",
"Epic fail",
"At least it's not BSoD...",
"User Error. Please Replace User",
"Brought to you by MaxED!",
"GZDoom Builder proudly presents:",
"You aren't expected to understand this",
"Back to the drawing board...",
"I'm sorry... :(",
"This is a horrbble day for you, and of course, the world",
"Abort, Retry, Fail?",
"You are making progress. I'm afraid that's something I can't allow to happen",
"You are making progress. That's not OK",
"No errors found, restarting computer",
"Does Not Compute!",
"Im sorry, Dave, Im afraid I cant do that",
"What's that? Chicken?",
"It can only be attributable to human error",
"It's now safe to turn off your computer",
"I've got a bad feeling about this",
"YOU CANT DO THAT!",
"Man the Lifeboats! Women and children first!",
"IMPOSSIBURU!!!",
"Now deleting all files. Goodbye",
"General Failure",
"Invalid Error",
"Beam me up Scotty, theres no life out here",
"Well, you ran into something and the game is over",
"I'm good at writing bad code",
"$FUNNY_ERROR_CAPTION",
"In Soviet Russia, exception throws YOU!",
"...and then GZDB was the demons!",
"B U S T E D",
"Freeze mode enabled",
"You feel strange...",
"That doesn't seem to work",
"This function is only available in the retail version of GZDoom Builder",
"You picked up the Random Exception.",
"Pinky says that you're the new hope. Bear that in mind.",
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"Deal with it",
"Error 47",
"YOU DIED",
"Thanks, Obama",
"The God Of Exceptions Demands MORE Exceptions!",
"Good. It's boring here anyway.",
"Shameful display!",
"It's CRASHENING!",
"W-W-W-WIPEOUT!",
"EVERYTHING IS LOST!",
"Your empty is full!",
"Let's see how far this infinite loop goes...",
};
this.Text = titles[new Random().Next(0, titles.Length - 1)]; this.Text = titles[new Random().Next(0, titles.Length - 1)];
bContinue.Enabled = !isterminating;
} }
private static string GetSystemInfo() private static string GetSystemInfo()
@ -181,8 +195,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
private void reportLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) private void reportLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{ {
if(!File.Exists(logPath)) return; if(!File.Exists(logpath)) return;
System.Diagnostics.Process.Start("explorer.exe", @"/select, " + logPath); System.Diagnostics.Process.Start("explorer.exe", @"/select, " + logpath);
reportLink.LinkVisited = true; reportLink.LinkVisited = true;
} }

View file

@ -2080,70 +2080,64 @@ namespace CodeImp.DoomBuilder
#region ================== mxd. Uncaught exceptions handling #region ================== mxd. Uncaught exceptions handling
// In some cases the program can remain operational after these
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{ {
try try
{ {
// Try handling it in user-friendy way...
GZBuilder.Windows.ExceptionDialog dlg = new GZBuilder.Windows.ExceptionDialog(e); GZBuilder.Windows.ExceptionDialog dlg = new GZBuilder.Windows.ExceptionDialog(e);
dlg.Setup(); dlg.Setup();
if(dlg.ShowDialog() == DialogResult.Cancel) Application.Exit(); if(dlg.ShowDialog() == DialogResult.Cancel) Terminate(false);
} }
catch catch
{ {
try string exceptionmsg;
{
MessageBox.Show("Fatal Windows Forms Error", "Fatal Windows Forms Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop); // Try getting exception details...
} try { exceptionmsg = "Fatal Windows Forms error occurred: " + e.Exception.Message + "\n\nStack Trace:\n" + e.Exception.StackTrace; }
finally catch(Exception exc) { exceptionmsg = "Failed to get initial excepton details: " + exc.Message + "\n\nStack Trace:\n" + exc.StackTrace; }
{
Application.Exit(); // Try logging it...
} try { WriteLogLine(exceptionmsg); } catch { }
// Try displaying it to the user...
try { MessageBox.Show("Fatal Windows Forms Error", exceptionmsg, MessageBoxButtons.OK, MessageBoxIcon.Stop); }
finally { Process.GetCurrentProcess().Kill(); }
} }
} }
// These are usually unrecoverable
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e) private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{ {
string exceptionmsg = string.Empty; try
try
{ {
Exception ex = (Exception)e.ExceptionObject; // Try handling it in user-friendy way...
exceptionmsg = "An application error occurred: " + ex.Message + "\n\nStack Trace:\n" + ex.StackTrace;
// Since we can't prevent the app from terminating, log this to the event log.
try
{
if(!EventLog.SourceExists("ThreadException"))
EventLog.CreateEventSource("ThreadException", "Application");
// Create an EventLog instance and assign its source.
using(EventLog myLog = new EventLog())
{
myLog.Source = "ThreadException";
myLog.WriteEntry(exceptionmsg);
}
}
catch(Exception exc)
{
MessageBox.Show("Could not write the error to the event log.\nReason: "
+ exc.Message + "\n\nInitial exception:\n" + exceptionmsg, "Fatal Non-UI Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
GZBuilder.Windows.ExceptionDialog dlg = new GZBuilder.Windows.ExceptionDialog(e); GZBuilder.Windows.ExceptionDialog dlg = new GZBuilder.Windows.ExceptionDialog(e);
dlg.Setup(); dlg.Setup();
dlg.ShowDialog(); if(dlg.ShowDialog() == DialogResult.Cancel) Terminate(false);
} }
catch(Exception exc) catch
{ {
try string exceptionmsg;
// Try getting exception details...
try
{ {
MessageBox.Show("Failed to write the error to the event log or to show the Exception Dialog.\n\nReason: " Exception ex = (Exception) e.ExceptionObject;
+ exc.Message + "\n\nInitial exception:\n" + exceptionmsg, "Fatal Non-UI Error", MessageBoxButtons.OK, MessageBoxIcon.Stop); exceptionmsg = "Fatal Non-UI error occurred: " + ex.Message + "\n\nStack Trace:\n" + ex.StackTrace;
}
finally
{
Application.Exit();
} }
catch(Exception exc)
{
exceptionmsg = "Failed to get initial excepton details: " + exc.Message + "\n\nStack Trace:\n" + exc.StackTrace;
}
// Try logging it...
try { WriteLogLine(exceptionmsg); } catch {}
// Try displaying it to the user...
try { MessageBox.Show("Fatal Windows Forms Error", exceptionmsg, MessageBoxButtons.OK, MessageBoxIcon.Stop); }
finally { Process.GetCurrentProcess().Kill(); }
} }
} }

View file

@ -724,21 +724,22 @@ namespace CodeImp.DoomBuilder
string settingsfile; string settingsfile;
WAD targetwad = null; WAD targetwad = null;
bool includenodes; bool includenodes;
bool fileexists = File.Exists(newfilepathname); //mxd
General.WriteLogLine("Saving map to file: " + newfilepathname); General.WriteLogLine("Saving map to file: " + newfilepathname);
//mxd. Official IWAD check... //mxd. Official IWAD check...
WAD hashtest = new WAD(newfilepathname, true); if(fileexists)
if(hashtest.IsOfficialIWAD)
{
General.WriteLogLine("Map saving aborted: attempt to modify official IWAD");
General.ShowErrorMessage("Official IWADs should not be modified.\nConsider making a PWAD instead", MessageBoxButtons.OK);
return false;
}
else
{ {
WAD hashtest = new WAD(newfilepathname, true);
if(hashtest.IsOfficialIWAD)
{
General.WriteLogLine("Map saving aborted: attempt to modify an official IWAD");
General.ShowErrorMessage("Official IWADs should not be modified.\nConsider making a PWAD instead", MessageBoxButtons.OK);
return false;
}
hashtest.Dispose(); hashtest.Dispose();
hashtest = null;
} }
// Scripts changed? // Scripts changed?
@ -786,79 +787,85 @@ namespace CodeImp.DoomBuilder
} }
//mxd. Target file is read-only? //mxd. Target file is read-only?
FileInfo info = new FileInfo(newfilepathname); if(fileexists)
if(info.Exists && info.IsReadOnly)
{ {
if(General.ShowWarningMessage("Unable to save the map: target file is read-only.\nRemove read-only flag and save the map anyway?", MessageBoxButtons.YesNo) == DialogResult.Yes) FileInfo info = new FileInfo(newfilepathname);
if(info.IsReadOnly)
{ {
General.WriteLogLine("Removing read-only flag from the map file..."); if(General.ShowWarningMessage("Unable to save the map: target file is read-only.\nRemove read-only flag and save the map anyway?", MessageBoxButtons.YesNo) == DialogResult.Yes)
try
{ {
info.IsReadOnly = false; General.WriteLogLine("Removing read-only flag from the map file...");
try
{
info.IsReadOnly = false;
}
catch(Exception e)
{
General.ShowErrorMessage("Failed to remove read-only flag from \"" + filepathname + "\":" + Environment.NewLine + Environment.NewLine + e.Message, MessageBoxButtons.OK);
General.WriteLogLine("Failed to remove read-only flag from \"" + filepathname + "\":" + e.Message);
return false;
}
} }
catch(Exception e) else
{ {
General.ShowErrorMessage("Failed to remove read-only flag from \"" + filepathname + "\":" + Environment.NewLine + Environment.NewLine + e.Message, MessageBoxButtons.OK); General.WriteLogLine("Map saving cancelled...");
General.WriteLogLine("Failed to remove read-only flag from \"" + filepathname + "\":" + e.Message);
return false; return false;
} }
} }
else
{
General.WriteLogLine("Map saving cancelled...");
return false;
}
} }
// Suspend data resources // Suspend data resources
data.Suspend(); data.Suspend();
//mxd. Check if the target file is locked //mxd. Check if the target file is locked
FileLockChecker.FileLockCheckResult checkresult = FileLockChecker.CheckFile(newfilepathname); if(fileexists)
if(!string.IsNullOrEmpty(checkresult.Error))
{ {
if(checkresult.Processes.Count > 0) FileLockChecker.FileLockCheckResult checkresult = FileLockChecker.CheckFile(newfilepathname);
if(!string.IsNullOrEmpty(checkresult.Error))
{ {
string rest = "Press 'Retry' to close " + (checkresult.Processes.Count > 1 ? "all processes" : "the process") if(checkresult.Processes.Count > 0)
+ " and retry." + Environment.NewLine + "Press 'Cancel' to cancel saving.";
if(General.ShowErrorMessage(checkresult.Error + rest, MessageBoxButtons.RetryCancel) == DialogResult.Retry)
{ {
// Close all processes string rest = "Press 'Retry' to close " + (checkresult.Processes.Count > 1 ? "all processes" : "the process")
foreach(Process process in checkresult.Processes) + " and retry." + Environment.NewLine + "Press 'Cancel' to cancel saving.";
{
try
{
if(!process.HasExited) process.Kill();
}
catch(Exception e)
{
General.ShowErrorMessage("Failed to close " + Path.GetFileName(process.MainModule.FileName) + ":" + Environment.NewLine + Environment.NewLine + e.Message, MessageBoxButtons.OK);
data.Resume();
General.WriteLogLine("Map saving failed: failed to close " + Path.GetFileName(process.MainModule.FileName));
return false;
}
}
// Retry if(General.ShowErrorMessage(checkresult.Error + rest, MessageBoxButtons.RetryCancel) == DialogResult.Retry)
data.Resume(); {
General.WriteLogLine("Map saving restarted..."); // Close all processes
return SaveMap(newfilepathname, purpose); foreach(Process process in checkresult.Processes)
{
try
{
if(!process.HasExited) process.Kill();
}
catch(Exception e)
{
General.ShowErrorMessage("Failed to close " + Path.GetFileName(process.MainModule.FileName) + ":" + Environment.NewLine + Environment.NewLine + e.Message, MessageBoxButtons.OK);
data.Resume();
General.WriteLogLine("Map saving failed: failed to close " + Path.GetFileName(process.MainModule.FileName));
return false;
}
}
// Retry
data.Resume();
General.WriteLogLine("Map saving restarted...");
return SaveMap(newfilepathname, purpose);
}
else
{
data.Resume();
General.WriteLogLine("Map saving cancelled...");
return false;
}
} }
else else
{ {
General.ShowErrorMessage(checkresult.Error, MessageBoxButtons.OK);
data.Resume(); data.Resume();
General.WriteLogLine("Map saving cancelled..."); General.WriteLogLine("Map saving failed: " + checkresult.Error);
return false; return false;
} }
} }
else
{
General.ShowErrorMessage(checkresult.Error, MessageBoxButtons.OK);
data.Resume();
General.WriteLogLine("Map saving failed: " + checkresult.Error);
return false;
}
} }
// Determine original map name // Determine original map name
@ -867,7 +874,7 @@ namespace CodeImp.DoomBuilder
try try
{ {
if(File.Exists(newfilepathname)) if(fileexists)
{ {
// mxd. Check if target wad already has a map with the same name // mxd. Check if target wad already has a map with the same name
if(purpose == SavePurpose.IntoFile) if(purpose == SavePurpose.IntoFile)

View file

@ -1,3 +1,4 @@
using System;
using System.Windows.Forms; using System.Windows.Forms;
using CodeImp.DoomBuilder.Controls; using CodeImp.DoomBuilder.Controls;
@ -407,6 +408,7 @@ namespace CodeImp.DoomBuilder.Windows
this.menufile.Name = "menufile"; this.menufile.Name = "menufile";
this.menufile.Size = new System.Drawing.Size(37, 20); this.menufile.Size = new System.Drawing.Size(37, 20);
this.menufile.Text = "&File"; this.menufile.Text = "&File";
this.menufile.DropDownOpening += menufile_DropDownOpening;
// //
// itemnewmap // itemnewmap
// //

View file

@ -2603,13 +2603,9 @@ namespace CodeImp.DoomBuilder.Windows
internal void AddRecentFile(string filename) internal void AddRecentFile(string filename)
{ {
//mxd. Recreate recent files list //mxd. Recreate recent files list
if(recentitems.Length != General.Settings.MaxRecentFiles) if(recentitems.Length != General.Settings.MaxRecentFiles)
{ {
foreach(ToolStripMenuItem item in recentitems) UpdateRecentItems();
menufile.DropDownItems.Remove(item);
SaveRecentFiles();
CreateRecentFiles();
} }
int movedownto = General.Settings.MaxRecentFiles - 1; int movedownto = General.Settings.MaxRecentFiles - 1;
@ -2645,6 +2641,16 @@ namespace CodeImp.DoomBuilder.Windows
itemnorecent.Visible = false; itemnorecent.Visible = false;
} }
//mxd
private void UpdateRecentItems()
{
foreach(ToolStripMenuItem item in recentitems)
menufile.DropDownItems.Remove(item);
SaveRecentFiles();
CreateRecentFiles();
}
// This returns the trimmed file/path string // This returns the trimmed file/path string
private string GetDisplayFilename(string filename) private string GetDisplayFilename(string filename)
{ {
@ -2689,6 +2695,12 @@ namespace CodeImp.DoomBuilder.Windows
// Open this file // Open this file
General.OpenMapFile(item.Tag.ToString(), null); General.OpenMapFile(item.Tag.ToString(), null);
} }
//mxd
private void menufile_DropDownOpening(object sender, EventArgs e)
{
UpdateRecentItems();
}
#endregion #endregion

View file

@ -1,7 +1,6 @@
#region ================== Namespaces #region ================== Namespaces
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data; using CodeImp.DoomBuilder.Data;

View file

@ -18,8 +18,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
using CodeImp.DoomBuilder.Data;
using System.IO; using System.IO;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Rendering; using CodeImp.DoomBuilder.Rendering;
#endregion #endregion
@ -45,10 +45,9 @@ namespace CodeImp.DoomBuilder.ZDoom
private readonly bool flipy; private readonly bool flipy;
private readonly float alpha; private readonly float alpha;
private readonly int rotation; //mxd private readonly int rotation; //mxd
private readonly TexturePathRenderStyle renderStyle; //mxd private readonly TexturePathRenderStyle renderstyle; //mxd
private readonly PixelColor blendColor; //mxd private readonly PixelColor blendcolor; //mxd
private readonly TexturePathBlendStyle blendStyle; //mxd private readonly TexturePathBlendStyle blendstyle; //mxd
private readonly float tintAmmount; //mxd
private static readonly string[] renderStyles = { "copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay" }; //mxd private static readonly string[] renderStyles = { "copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay" }; //mxd
private readonly bool skip; //mxd private readonly bool skip; //mxd
@ -63,10 +62,9 @@ namespace CodeImp.DoomBuilder.ZDoom
public bool FlipY { get { return flipy; } } public bool FlipY { get { return flipy; } }
public float Alpha { get { return alpha; } } public float Alpha { get { return alpha; } }
public int Rotation { get { return rotation; } } //mxd public int Rotation { get { return rotation; } } //mxd
public TexturePathRenderStyle RenderStyle { get { return renderStyle; } } //mxd public TexturePathRenderStyle RenderStyle { get { return renderstyle; } } //mxd
public TexturePathBlendStyle BlendStyle { get { return blendStyle; } } public TexturePathBlendStyle BlendStyle { get { return blendstyle; } }
public float TintAmmount { get { return tintAmmount; } } public PixelColor BlendColor { get { return blendcolor; } }//mxd
public PixelColor BlendColor { get { return blendColor; } }//mxd
public bool Skip { get { return skip; } } //mxd public bool Skip { get { return skip; } } //mxd
#endregion #endregion
@ -78,8 +76,8 @@ namespace CodeImp.DoomBuilder.ZDoom
{ {
// Initialize // Initialize
alpha = 1.0f; alpha = 1.0f;
renderStyle = TexturePathRenderStyle.Copy;//mxd renderstyle = TexturePathRenderStyle.COPY;//mxd
blendStyle = TexturePathBlendStyle.None; //mxd blendstyle = TexturePathBlendStyle.NONE; //mxd
// There should be 3 tokens separated by 2 commas now: // There should be 3 tokens separated by 2 commas now:
// Name, Width, Height // Name, Width, Height
@ -164,30 +162,69 @@ namespace CodeImp.DoomBuilder.ZDoom
string s; string s;
if(!ReadTokenString(parser, token, out s)) return; if(!ReadTokenString(parser, token, out s)) return;
int index = Array.IndexOf(renderStyles, s.ToLowerInvariant()); int index = Array.IndexOf(renderStyles, s.ToLowerInvariant());
renderStyle = index == -1 ? TexturePathRenderStyle.Copy : (TexturePathRenderStyle) index; renderstyle = index == -1 ? TexturePathRenderStyle.COPY : (TexturePathRenderStyle) index;
break; break;
case "blend": //mxd case "blend": //mxd
int val;
if(!ReadTokenColor(parser, token, out val)) return;
blendColor = PixelColor.FromInt(val);
parser.SkipWhitespace(false); parser.SkipWhitespace(false);
token = parser.ReadToken(); PixelColor color = new PixelColor();
if(token == ",") //read tint ammount // Blend <string color>[,<float alpha>] block?
{ token = parser.ReadToken(false);
parser.SkipWhitespace(false); if(!parser.ReadByte(token, ref color.r))
if(!ReadTokenFloat(parser, token, out tintAmmount)) return;
tintAmmount = General.Clamp(tintAmmount, 0.0f, 1.0f);
blendStyle = TexturePathBlendStyle.Tint;
}
else
{ {
blendStyle = TexturePathBlendStyle.Blend; if(!ZDTextParser.GetColorFromString(token, ref color))
// Rewind so this structure can be read again {
parser.DataStream.Seek(-token.Length, SeekOrigin.Current); parser.ReportError("Unsupported patch blend definition");
return;
}
} }
// That's Blend <int r>,<int g>,<int b>[,<float alpha>] block
else
{
if(!parser.SkipWhitespace(false) ||
!parser.NextTokenIs(",", false) || !parser.SkipWhitespace(false) || !parser.ReadByte(ref color.g) ||
!parser.NextTokenIs(",", false) || !parser.SkipWhitespace(false) || !parser.ReadByte(ref color.b))
{
parser.ReportError("Unsupported patch blend definition");
return;
}
}
// Alpha block?
float blendalpha = -1f;
parser.SkipWhitespace(false);
if(parser.NextTokenIs(",", false))
{
parser.SkipWhitespace(false);
if(!ReadTokenFloat(parser, token, out blendalpha))
{
parser.ReportError("Unsupported patch blend alpha value");
return;
}
}
// Blend may never be 0 when using the Tint effect
if(blendalpha > 0.0f)
{
color.a = (byte)General.Clamp((int)(blendalpha * 255), 1, 254);
blendstyle = TexturePathBlendStyle.TINT;
}
else if(blendalpha < 0.0f)
{
color.a = 255;
blendstyle = TexturePathBlendStyle.BLEND;
}
else
{
// Ignore Blend when alpha == 0
parser.LogWarning("Blend with zero alpha will be ignored by ZDoom");
break;
}
// Store the color
blendcolor = color;
break; break;
case "}": case "}":
@ -268,36 +305,6 @@ namespace CodeImp.DoomBuilder.ZDoom
return true; return true;
} }
//mxd. This reads the next token and sets a PixelColor value, returns false when failed
private static bool ReadTokenColor(TexturesParser parser, string propertyname, out int value)
{
parser.SkipWhitespace(true);
string strvalue = parser.StripTokenQuotes(parser.ReadToken());
value = 0;
if(string.IsNullOrEmpty(strvalue))
{
// Can't find the property value!
parser.ReportError("Expected a value for property \"" + propertyname + "\"");
return false;
}
if(strvalue[0] != '#')
{
parser.ReportError("Expected color value for property \"" + propertyname + "\"");
return false;
}
// Try parsing as value
if(!int.TryParse(strvalue.Remove(0, 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out value))
{
parser.ReportError("Expected color value for property \"" + propertyname + "\"");
return false;
}
return true;
}
#endregion #endregion
} }
} }

View file

@ -0,0 +1,87 @@
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Globalization;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Rendering;
#endregion
namespace CodeImp.DoomBuilder.ZDoom
{
internal sealed class X11R6RGBParser : ZDTextParser
{
#region ================== Variables
private readonly Dictionary<string, PixelColor> knowncolors;
#endregion
#region ================== Properties
internal override ScriptType ScriptType { get { return ScriptType.X11R6RGB; } }
public Dictionary<string, PixelColor> KnownColors { get { return knowncolors; } }
#endregion
#region ================== Constructor
internal X11R6RGBParser()
{
knowncolors = new Dictionary<string, PixelColor>(StringComparer.OrdinalIgnoreCase);
}
#endregion
#region ================== Parsing
public override bool Parse(TextResourceData data, bool clearerrors)
{
// Already parsed?
if(!base.AddTextResource(data))
{
if(clearerrors) ClearError();
return true;
}
// Cannot process?
if(!base.Parse(data, clearerrors)) return false;
// Continue until at the end of the stream
char[] space = {' ', '\t'};
while(SkipWhitespace(true))
{
string line = ReadLine();
if(string.IsNullOrEmpty(line) || line.StartsWith("!")) continue; // Skip comments
// "R G B Name with spaces"
string[] parts = line.Split(space, StringSplitOptions.RemoveEmptyEntries);
if(parts.Length < 4)
{
ReportError("Incorrect X11R6RGB color assignment");
return false;
}
// Parse colors
byte r = 0, g = 0, b = 0;
if(!ReadByte(parts[0], ref r)) { ReportError("Expected red color value in [0 .. 255] range"); return false; }
if(!ReadByte(parts[1], ref g)) { ReportError("Expected green color value in [0 .. 255] range"); return false; }
if(!ReadByte(parts[2], ref b)) { ReportError("Expected blue color value in [0 .. 255] range"); return false; }
// Assemble name
string colorname = string.Join("", parts, 3, parts.Length - 3);
// Add to collection
knowncolors[colorname] = new PixelColor(255, r, g, b);
}
return true;
}
#endregion
}
}

View file

@ -24,6 +24,7 @@ using System.IO;
using CodeImp.DoomBuilder.Compilers; using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data; using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Rendering;
#endregion #endregion
@ -557,6 +558,65 @@ namespace CodeImp.DoomBuilder.ZDoom
return success; return success;
} }
//mxd
protected internal bool ReadByte(ref byte value) { return ReadByte(StripTokenQuotes(ReadToken(false)), ref value); }
protected internal bool ReadByte(string token, ref byte value)
{
if(token == "-") return false;
int result;
if(!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out result) || result < 0 || result > 255)
{
return false;
}
value = (byte)result;
return true;
}
//mxd. This replicates ZDoom's V_GetColorFromString method
public static bool GetColorFromString(string name, ref PixelColor color)
{
name = StripQuotes(name.Replace(" ", ""));
// Check for HTML-style #RRGGBB or #RGB color string
bool ishtmlcolor = false;
if(name.StartsWith("#"))
{
ishtmlcolor = true;
name = name.Remove(0, 1);
// Expand RGB to RRGGBB
if(name.Length == 3)
{
name = name[0].ToString() + name[0] + name[1] + name[1] + name[2] + name[2];
}
else if(name.Length != 6)
{
// Bad HTML-style; pretend it's black.
color = new PixelColor();
return true;
}
}
// Probably it's a hex color (like FFCC11)?
int ci;
if(int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ci))
{
color = PixelColor.FromInt(ci).WithAlpha(255);
return true;
}
// Probably it's a color name?
if(!ishtmlcolor && General.Map.Data.KnownColors.ContainsKey(name))
{
color = General.Map.Data.KnownColors[name];
return true;
}
return false;
}
//mxd //mxd
protected void SkipStructure() { SkipStructure(new HashSet<string>()); } protected void SkipStructure() { SkipStructure(new HashSet<string>()); }
protected void SkipStructure(HashSet<string> breakat) protected void SkipStructure(HashSet<string> breakat)