UltimateZoneBuilder/Source/Core/Data/Scripting/ScriptResource.cs
MaxED 88363a1a66 Added, Script Editor: added "Find usages" option (available in the "Search" menu, via text editor context menu, via Ctrl-Shift-F shortcut and in the "Find and Replace" window).
Added, Script Editor: double-clicking text resource tab header now shows the corresponding resource in the Resources tree.
Updated, Game configurations, UDMF: added several missing Thing renderstyles.
2016-12-08 12:10:43 +00:00

191 lines
5.6 KiB
C#

#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Controls;
using CodeImp.DoomBuilder.Windows;
#endregion
namespace CodeImp.DoomBuilder.Data.Scripting
{
public sealed class ScriptResource
{
#region ================== Variables
private string filename;
private string filepathname;
private string resourcedisplayname;
private int lumpindex = -1;
private DataReader resource;
private string resourcepath;
private HashSet<string> entries;
private ScriptType scripttype;
private bool isreadonly;
// Special cases...
private string parentresourcelocation;
#endregion
#region ================== Properties
public string Filename { get { return filename; } } // Path to text file inside of Resource
public string FilePathName { get { return filepathname; } } // Resource location and file path inside resource combined
public int LumpIndex { get { return lumpindex; } } // Text lump index if Resource is wad, -1 otherwise
internal DataReader Resource { get { return GetResource(); } }
public HashSet<string> Entries { get { return entries; } } // Actors/models/sounds etc.
public ScriptType ScriptType { get { return scripttype; } }
public bool IsReadOnly { get { return isreadonly; } }
#endregion
#region ================== Constructor
public ScriptResource(TextResourceData source, ScriptType type)
{
resource = source.Source;
resourcepath = resource.Location.location;
resourcedisplayname = resource.Location.GetDisplayName();
filename = source.Filename.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
filepathname = Path.Combine(resourcepath, filename);
entries = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
lumpindex = source.LumpIndex;
scripttype = type;
isreadonly = resource.IsReadOnly;
// Embedded resources require additional tender loving care...
if(resource is WADReader)
{
WADReader wr = (WADReader)resource;
if(wr.ParentResource is PK3Reader)
parentresourcelocation = wr.ParentResource.Location.location;
}
}
#endregion
#region ================== Methods
internal bool ContainsText(FindReplaceOptions options)
{
// Get text
DataReader res = GetResource();
if(res == null) return false;
MemoryStream stream = res.LoadFile(filename, lumpindex);
if(stream != null)
{
// Add word boundary delimiter?
string findtext = (options.WholeWord ? "\\b" + options.FindText + "\\b" : options.FindText);
RegexOptions ro = (options.CaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
Regex regex = new Regex(findtext, ro);
using(StreamReader reader = new StreamReader(stream, ScriptEditorControl.Encoding))
{
while(!reader.EndOfStream)
{
if(regex.IsMatch(reader.ReadLine())) return true;
}
}
}
// No dice...
return false;
}
// Finds text occurencies in the resource. Whole word / ignode case only.
internal List<FindUsagesResult> FindUsages(FindReplaceOptions options)
{
var result = new List<FindUsagesResult>();
// Get text
DataReader res = GetResource();
if(res == null) return result;
MemoryStream stream = res.LoadFile(filename, lumpindex);
if(stream != null)
{
// Add word boundary delimiter
string findtext = (options.WholeWord ? "\\b" + options.FindText + "\\b" : options.FindText);
Regex regex = new Regex(findtext, (options.CaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase));
using(StreamReader reader = new StreamReader(stream, ScriptEditorControl.Encoding))
{
int lineindex = 0;
while(!reader.EndOfStream)
{
string line = reader.ReadLine();
foreach(Match match in regex.Matches(line))
result.Add(new FindUsagesResult(this, match, line, lineindex));
lineindex++;
}
}
}
return result;
}
private DataReader GetResource()
{
if(resource == null || resource.IsDisposed)
{
resource = null;
// Try to re-aquire resource
if(!string.IsNullOrEmpty(parentresourcelocation))
{
// Special case: WAD resource inside of PK3 resource.
// Resource resourcepath will be different after reloading resources, because it's randomly generated.
// So resolve using displayname and parent resource location...
foreach(DataReader reader in General.Map.Data.Containers)
{
// Found parent
if(reader.Location.location == parentresourcelocation && reader is PK3Reader)
{
PK3Reader pr = (PK3Reader)reader;
foreach(WADReader wr in pr.Wads)
{
if(wr.Location.GetDisplayName() == resourcedisplayname)
{
// Found it
resource = reader;
// Some paths need updating...
resourcepath = resource.Location.location;
filepathname = Path.Combine(resourcepath, filename);
break;
}
}
}
}
}
else
{
foreach(DataReader reader in General.Map.Data.Containers)
{
if(reader.Location.location == resourcepath)
{
// Found it
resource = reader;
break;
}
}
}
}
return resource;
}
// Used as tab and script navigator item title
public override string ToString()
{
return (lumpindex != -1 ? lumpindex + ":" : "") + Path.GetFileName(filename);
}
#endregion
}
}