private NewMesh LoadArmorMesh(IBinder partsbnd) { List <TPF> tpfs = new List <TPF>(); FLVER2 flver = null; foreach (var f in partsbnd.Files) { string nameCheck = f.Name.ToLower(); if (nameCheck.EndsWith(".tpf") || TPF.Is(f.Bytes)) { tpfs.Add(TPF.Read(f.Bytes)); } else if (flver == null && nameCheck.EndsWith(".flver") || FLVER2.Is(f.Bytes)) { flver = FLVER2.Read(f.Bytes); } } foreach (var tpf in tpfs) { TexturePool.AddTpf(tpf); } NewMesh mesh = null; if (flver != null) { mesh = new NewMesh(flver, false, boneIndexRemap); } Scene.RequestTextureLoad(); return(mesh); }
private int PackTPF(TPF tpf, string relOutputDir) { if (tpf.Platform != TPF.TPFPlatform.PC) { return(0); } int textureCount = 0; foreach (TPF.Texture texture in tpf) { try { string overridePath = $@"{Game.Settings.PackDirectory.TrimEnd('\\')}\{relOutputDir}\{texture.Name}.dds"; if (File.Exists(overridePath)) { texture.Bytes = File.ReadAllBytes(overridePath); textureCount++; } } catch (Exception ex) { throw new Exception($"Error in texture \"{texture.Name}\"", ex); } } return(textureCount); }
public void fillDataGridView(TPF menuTPF, DRB menuDRB) { iconBindingSource.Clear(); textures = new List <string>(); foreach (TPF.Texture entry in menuTPF.Textures) { textures.Add(entry.Name); } List <string> sortedNames = new List <string>(textures); sortedNames.Sort(); dgvIconsTextureCol.DataSource = sortedNames; drb = menuDRB; sprites = new List <SpriteWrapper>(); DRB.Dlg icons = menuDRB.Dlgs.Find(dlg => dlg.Name == "Icon"); foreach (DRB.Dlgo dlgo in icons.Dlgos.Where(dlgo => dlgo.Shape is DRB.Shape.Sprite)) { sprites.Add(new SpriteWrapper(dlgo, textures)); } sprites.Sort(); iconBindingSource.DataSource = sprites; }
public (bool TextureExists, IntPtr TextureHandle) GetFfxTextureIntPtr(int textureId) { if (!_loadedTexturesDictionary.ContainsKey(textureId)) { var a = _ffxTexturesIEnumerable.Where(item => item.Name.Contains($"s{textureId.ToString("00000")}.tpf")); if (a.Any()) { var tpfBytes = a.First().Bytes; var tpfTexturesList = TPF.Read(tpfBytes).Textures; if (tpfTexturesList.Any()) { var ddsBytes = tpfTexturesList.First().Bytes; DdsImage ddsImage = new DdsImage(ddsBytes); Image <Rgba32> image = Image.LoadPixelData <Rgba32>(ddsImage.Data, ddsImage.Width, ddsImage.Height); var img = new ImageSharpTexture(image); var veldridTexture = img.CreateDeviceTexture(MainUserInterface.Gd, MainUserInterface.Gd.ResourceFactory); var textureHandle = MainUserInterface.Controller.GetOrCreateImGuiBinding(MainUserInterface.Gd.ResourceFactory, veldridTexture); veldridTexture.Dispose(); _loadedTexturesDictionary.Add(textureId, textureHandle); return(true, textureHandle); } } return(false, IntPtr.Zero); } else { return(true, _loadedTexturesDictionary[textureId]); } }
private int UnpackTPF(TPF tpf, string relOutputDir, UnpackReport report) { if (tpf.Platform != TPF.TPFPlatform.PC) { return(0); } foreach (TPF.Texture texture in tpf) { try { string dir = $@"{Game.Settings.UnpackDirectory.TrimEnd('\\')}\{relOutputDir}"; string path = $@"{dir}\{texture.Name}.dds"; Directory.CreateDirectory(dir); File.WriteAllBytes(path, texture.Bytes); report.Files.Add(new UnpackReportFile(texture)); } catch (Exception ex) { throw new Exception($"Error in texture \"{texture.Name}\"", ex); } } return(tpf.Textures.Count); }
public LoadTPFResourcesTask(string virtpathbase, TPF tpf, AccessLevel al, GameType type) { _virtpathbase = virtpathbase; _tpf = tpf; _accessLevel = al; _game = type; }
public static void AddTpf(TPF tpf) { foreach (var tex in tpf.Textures) { AddFetch(tpf, tex.Name); } }
private void repackTPF(TPF tpf, string baseDir, string subDir) { // parts\HR_F_0010 and parts\HR_F_0010_M have duplicate filenames in the same tpf // thx QLOC List <string> names = new List <string>(); List <string> dupes = new List <string>(); foreach (TPF.Texture tpfEntry in tpf.Textures) { if (names.Contains(tpfEntry.Name)) { dupes.Add(tpfEntry.Name); } else { names.Add(tpfEntry.Name); } } for (int i = 0; i < tpf.Textures.Count; i++) { TPF.Texture tpfEntry = tpf.Textures[i]; string name = tpfEntry.Name; if (dupes.Contains(name)) { name += "_" + i; } repackFile(tpfEntry, name, baseDir, subDir); } }
public static void AddTpfsFromPaths(List <string> paths, IProgress <double> progress) { var tpfList = new List <TPF>(); int totalTexCount = 0; for (int i = 0; i < paths.Count; i++) { if (File.Exists(paths[i])) { TPF tpf = SoulsFormats.TPF.Read(paths[i]); tpfList.Add(tpf); totalTexCount += tpf.Textures.Count; } else { Console.WriteLine($"Warning: TPF '{paths[i]}' does not exist."); } } double texIndex = 0; for (int i = 0; i < tpfList.Count; i++) { for (int j = 0; j < tpfList[i].Textures.Count; j++) { AddFetchTPF(tpfList[i], Utils.GetShortIngameFileName(tpfList[i].Textures[j].Name).ToLower()); progress?.Report(++texIndex / totalTexCount); } } progress?.Report(1); }
public void Dispose() { TPFReference = null; CachedTexture?.Dispose(); CachedTexture = null; }
public static void Repack(string sourceDir, string targetDir) { TPF tpf = new TPF(); XmlDocument xml = new XmlDocument(); xml.Load($"{sourceDir}\\_yabber-tpf.xml"); string filename = xml.SelectSingleNode("tpf/filename").InnerText; Enum.TryParse(xml.SelectSingleNode("tpf/compression")?.InnerText ?? "None", out DCX.Type compression); tpf.Compression = compression; tpf.Encoding = Convert.ToByte(xml.SelectSingleNode("tpf/encoding").InnerText, 16); tpf.Flag2 = Convert.ToByte(xml.SelectSingleNode("tpf/flag2").InnerText, 16); foreach (XmlNode texNode in xml.SelectNodes("tpf/textures/texture")) { string name = Path.GetFileNameWithoutExtension(texNode.SelectSingleNode("name").InnerText); byte format = Convert.ToByte(texNode.SelectSingleNode("format").InnerText, 16); byte flags1 = Convert.ToByte(texNode.SelectSingleNode("flags1").InnerText, 16); int flags2 = Convert.ToInt32(texNode.SelectSingleNode("flags2").InnerText, 16); byte[] bytes = File.ReadAllBytes($"{sourceDir}\\{name}.dds"); tpf.Textures.Add(new TPF.Texture(name, format, flags1, bytes)); } string outPath = $"{targetDir}\\{filename}"; YBUtil.Backup(outPath); tpf.Write(outPath); }
public static void AddTpfFromPath(string path) { if (File.Exists(path)) { TPF tpf = SoulsFormats.TPF.Read(path); AddTpf(tpf); } }
/// <summary> /// Returns a delegate that wraps the specified, safe /// parser in order to support custom defined parsers. /// </summary> /// <param name="targetType">The target type of the parser.</param> /// <param name="parser">The original parser to wrap.</param> /// <returns> /// A safe parser delegate that supports custom parsers and falls /// back to calling the specified <paramref name="parser" />. /// </returns> private static TPF <T> InitTryParse(Type targetType, TPF <T> parser) { return(!CustomParser.TryGetParser(targetType, out TPF <T> custom) ? parser : (string s, IFormatProvider provider, out T result) => { return custom(s, provider, out result) || parser(s, provider, out result); }); }
public static void AddTpf(TPF tpf, IProgress <double> progress = null) { double i = 0; foreach (var tex in tpf.Textures) { AddFetchTPF(tpf, tex.Name.ToLower()); progress?.Report(++i / tpf.Textures.Count); } }
/// <summary> /// Gets the parser that targets the specified type. /// </summary> /// <typeparam name="T">The target type of the parser.</typeparam> /// <param name="type"> /// The runtime instance of the parser's target type. /// </param> /// <param name="parser"> /// When this method returns, contains the parser /// for the type <typeparamref name="T" />, if a /// parser is found; otherwise, <c>null</c>. /// </param> /// <returns> /// <c>true</c>, if a parser for type <typeparamref name="T" /> /// is found; otherwise, <c>false</c>. /// </returns> public static bool TryGetParser <T>(Type type, out TPF <T> parser) { if (parsers.TryGetValue(type, out var p)) { parser = p as TPF <T>; return(true); } parser = null; return(false); }
private int PackBinder(IBinder binder, string relOutputDir, CancellationToken cancelToken) { int textureCount = 0; foreach (BinderFile file in binder.Files) { if (cancelToken.IsCancellationRequested) { return(textureCount); } if (TPUtil.HasValidExtension(file.Name)) { try { byte[] bytes = file.Bytes; DCX.Type dcxType = DCX.Type.None; if (DCX.Is(bytes)) { bytes = DCX.Decompress(bytes, out dcxType); } if (TPF.IsRead(bytes, out TPF tpf)) { int thisTextureCount = PackTPF(tpf, relOutputDir); if (thisTextureCount > 0) { file.Bytes = tpf.Write(dcxType); } textureCount += thisTextureCount; } else if (BND4.IsRead(bytes, out BND4 bnd)) { int thisTextureCount = PackBinder(bnd, relOutputDir, cancelToken); if (thisTextureCount > 0) { file.Bytes = bnd.Write(dcxType); } textureCount += thisTextureCount; } else { throw new NotSupportedException("Unknown file type."); } } catch (Exception ex) { throw new Exception($"Error in binder file \"{file.Name}\"", ex); } } } return(textureCount); }
public static void AddSpecificTexturesFromBinder(string name, List <string> textures, bool directEntryNameMatch = false) { if (!File.Exists(name)) { return; } IBinder bnd = null; if (BXF4.IsBHD(name)) { bnd = BXF4.Read(name, name.Substring(0, name.Length - 7) + ".tpfbdt"); } else if (BXF4.IsBDT(name)) { bnd = BXF4.Read(name.Substring(0, name.Length - 7) + ".tpfbhd", name); } else if (BXF3.IsBHD(name)) { bnd = BXF3.Read(name, name.Substring(0, name.Length - 7) + ".tpfbdt"); } else if (BXF3.IsBDT(name)) { bnd = BXF3.Read(name.Substring(0, name.Length - 7) + ".tpfbhd", name); } else if (BND4.Is(name)) { bnd = BND4.Read(name); } else if (BND3.Is(name)) { bnd = BND3.Read(name); } foreach (var f in bnd.Files) { if (directEntryNameMatch ? textures.Contains(Utils.GetShortIngameFileName(f.Name).ToLower()) : TPF.Is(f.Bytes)) { var tpf = TPF.Read(f.Bytes); foreach (var tx in tpf.Textures) { var shortTexName = Utils.GetShortIngameFileName(tx.Name).ToLower(); if (textures.Contains(shortTexName)) { AddFetchTPF(tpf, tx.Name.ToLower()); textures.Remove(shortTexName); } } } } }
public static void AddSpecificTexturesFromBXF3(string name, List <string> textures) { var bxf = BXF3.Read(name, name.Substring(0, name.Length - 7) + ".tpfbdt"); foreach (var f in bxf.Files) { if (f.Name != null && f.Name.ToLower().Contains(".tpf") && textures.Contains(Utils.GetShortIngameFileName(f.Name.ToLower()))) { AddTpf(TPF.Read(f.Bytes)); } } }
public static void Unpack(this TPF tpf, string sourceName, string targetDir, IProgress <float> progress) { #if !DEBUG if (tpf.Platform != TPF.TPFPlatform.PC) { throw new NotSupportedException("Yabber does not support console TPFs at the moment."); } #endif Directory.CreateDirectory(targetDir); var xws = new XmlWriterSettings(); xws.Indent = true; var xw = XmlWriter.Create($"{targetDir}\\_yabber-tpf.xml", xws); xw.WriteStartElement("tpf"); xw.WriteElementString("filename", sourceName); xw.WriteElementString("compression", tpf.Compression.ToString()); xw.WriteElementString("encoding", $"0x{tpf.Encoding:X2}"); xw.WriteElementString("flag2", $"0x{tpf.Flag2:X2}"); xw.WriteStartElement("textures"); for (int i = 0; i < tpf.Textures.Count; i++) { TPF.Texture texture = tpf.Textures[i]; xw.WriteStartElement("texture"); xw.WriteElementString("name", texture.Name + ".dds"); xw.WriteElementString("format", texture.Format.ToString()); xw.WriteElementString("flags1", $"0x{texture.Flags1:X2}"); if (texture.FloatStruct != null) { xw.WriteStartElement("FloatStruct"); xw.WriteAttributeString("Unk00", texture.FloatStruct.Unk00.ToString()); foreach (float value in texture.FloatStruct.Values) { xw.WriteElementString("Value", value.ToString()); } xw.WriteEndElement(); } xw.WriteEndElement(); File.WriteAllBytes($"{targetDir}\\{texture.Name}.dds", texture.Headerize()); progress.Report((float)i / tpf.Textures.Count); } xw.WriteEndElement(); xw.WriteEndElement(); xw.Close(); }
private void UnpackTPFs(ConcurrentQueue <string> filepaths) { string filepath; while (filepaths.TryDequeue(out filepath)) { Log.Enqueue("Unpacking texture file " + (fileCount - filepaths.Count) + " of " + fileCount); // These are already full paths, but trust no one, not even yourself string absolute = Path.GetFullPath(filepath); string relative = absolute.Substring(gameDir.Length + 1); byte[] bytes = File.ReadAllBytes(absolute); string extension = Path.GetExtension(absolute); string subpath = Path.GetDirectoryName(relative) + "\\" + Path.GetFileNameWithoutExtension(absolute); if (extension == ".dcx") { bytes = DCX.Decompress(bytes); extension = Path.GetExtension(Path.GetFileNameWithoutExtension(absolute)); subpath = subpath.Substring(0, subpath.Length - extension.Length); } switch (extension) { case ".tpf": TPF tpf = TPF.Read(bytes); UnpackTPF(tpf, looseDir, subpath); break; case ".chrbnd": case ".ffxbnd": case ".fgbnd": case ".objbnd": case ".partsbnd": BND3 bnd = BND3.Read(bytes); foreach (var entry in bnd.Files) { string entryExtension = Path.GetExtension(entry.Name); if (entryExtension == ".tpf") { TPF bndTPF = TPF.Read(entry.Bytes); UnpackTPF(bndTPF, looseDir, subpath); } } break; } } }
public List <string> GetEnvMapTextureNames(string mapid) { var l = new List <string>(); if (Type == GameType.DarkSoulsIII) { var mid = mapid.Substring(0, 3); var t = TPF.Read(GetAssetPath($@"map\{mid}\{mid}_envmap.tpf.dcx")); foreach (var tex in t.Textures) { l.Add(tex.Name); } } return(l); }
public static void CombineDragonTpfs() { // Utility for creating Divine Dragon texbnd. Requires using Yabber to unpack these bnds, and repack after done. string gamePath = GameSpec.ForGame(GameSpec.FromGame.SDT).GameDir; string mainPath = $@"{gamePath}\chr\c5200-texbnd-dcx\chr\c5200\c5200.tpf"; SFUtil.Backup(mainPath); TPF dragon = TPF.Read(mainPath); foreach (string p in Directory.GetFiles($@"{gamePath}\map\m25\m25_0000-tpfbhd", "m25_Dragon*.tpf.dcx")) { TPF t = TPF.Read(p); dragon.Textures.AddRange(t.Textures); } dragon.Write(mainPath); }
public static void AddTextureBnd(IBinder bnd, IProgress <double> prog) { var tpfs = bnd.Files.Where(file => file.Name != null && (file.Name.ToLower().EndsWith(".tpf") || file.Name.ToLower().EndsWith(".tpf.dcx"))).ToList(); var tbnds = bnd.Files.Where(file => file.Name != null && file.Name.ToLower().EndsWith(".tbnd")).ToList(); double total = tpfs.Count + tbnds.Count; double tpfFraction = 0; double tbndFraction = 0; if (total > 0) { tpfFraction = tpfs.Count / total; tbndFraction = tbnds.Count / total; } for (int i = 0; i < tpfs.Count; i++) { var file = tpfs[i]; if (file.Bytes.Length > 0) { TPF tpf = TPF.Read(file.Bytes); AddTpf(tpf); } prog?.Report(i / tpfFraction); } for (int i = 0; i < tbnds.Count; i++) { var file = tbnds[i]; if (file.Bytes.Length > 0) { IBinder tbnd = BND3.Read(file.Bytes); for (int j = 0; j < tbnd.Files.Count; j++) { TPF tpf = TPF.Read(tbnd.Files[j].Bytes); AddTpf(tpf); prog?.Report(tpfFraction + i / tbndFraction + j / tbnd.Files.Count * (tbndFraction / tbnds.Count)); } } prog?.Report(tpfFraction + i / tbndFraction); } prog?.Report(1); }
private bool processTPF(TPF tpf, string baseDir, string subDir) { // parts\HR_F_0010 and parts\HR_F_0010_M have duplicate filenames in the same tpf // thx QLOC List <string> names = new List <string>(); List <string> dupes = new List <string>(); foreach (TPF.Texture tpfEntry in tpf.Textures) { if (names.Contains(tpfEntry.Name)) { dupes.Add(tpfEntry.Name); } else { names.Add(tpfEntry.Name); } } bool edited = false; for (int i = 0; i < tpf.Textures.Count; i++) { if (stop) { return(false); } TPF.Texture tpfEntry = tpf.Textures[i]; string name = tpfEntry.Name; if (dupes.Contains(name)) { name += "_" + i; } if (repack) { edited |= repackFile(tpfEntry, name, baseDir, subDir); } else { unpackFile(tpfEntry, name, baseDir, subDir); } } return(edited); }
private void loadFiles(string gameDir, bool silent = false) { if (File.Exists($@"{gameDir}\DARKSOULS.exe")) { remastered = false; } else if (File.Exists($@"{gameDir}\DarkSoulsRemastered.exe")) { remastered = true; } else { ShowError($"Dark Souls executable not found in directory:\n{gameDir}", silent); return; } TPF menuTPF; string tpfPath = $"{gameDir}{TPF_PATH}{(remastered ? ".dcx" : "")}"; try { menuTPF = TPF.Read(tpfPath); } catch (Exception ex) { ShowError($"Failed to read TPF:\n{tpfPath}\n\n{ex}", silent); return; } DRB menuDRB; string drbPath = $"{gameDir}{DRB_PATH}{(remastered ? ".dcx" : "")}"; try { menuDRB = DRB.Read(drbPath, remastered); } catch (Exception ex) { ShowError($"Failed to read DRB:\n{drbPath}{ex}", silent); return; } fillDataGridView(menuTPF, menuDRB); enableControls(true); }
public static void AddMapTexBXF4(int area, IProgress <double> prog) { var dir = InterrootLoader.GetInterrootPath($"map\\m{area:D2}"); if (!Directory.Exists(dir)) { return; } var mapTpfFileNames = Directory.GetFiles(dir, "*.tpfbhd"); int fileIndex = 0; foreach (var t in mapTpfFileNames) { BXF4 bxf = null; lock (_lock_IO) { bxf = BXF4.Read(t, t.Substring(0, t.Length - 7) + ".tpfbdt"); } for (int i = 0; i < bxf.Files.Count; i++) { if (bxf.Files[i].Name.Contains(".tpf")) { var tpf = TPF.Read(bxf.Files[i].Bytes); foreach (var tn in tpf.Textures) { AddFetch(tpf, tn.Name); } tpf = null; } GFX.ModelDrawer.RequestTextureLoad(); // Report each subfile as a tiny part of the bar prog?.Report((1.0 * fileIndex / mapTpfFileNames.Length) + ((1.0 / mapTpfFileNames.Length) * ((i + 1.0) / bxf.Files.Count))); } bxf = null; fileIndex++; prog?.Report((1.0 * fileIndex / mapTpfFileNames.Length)); } GFX.ModelDrawer.RequestTextureLoad(); }
public ImGuiFxrTextureHandler(BND4 ffxResourcesBin) { this._ffxTexturesIEnumerable = ffxResourcesBin.Files.Where(item => item.Name.Contains("sfx\\tex")); foreach (var binderFileTpf in _ffxTexturesIEnumerable) { var tpfBytes = binderFileTpf.Bytes; var tpfTexturesList = TPF.Read(tpfBytes).Textures; if (tpfTexturesList.Any()) { var tpf = tpfTexturesList.First(); if (int.TryParse(tpf.Name.TrimStart('s'), out int textureId)) { FfxTexturesIdList.Add(textureId); } } } FfxTexturesIdList.Sort(); LoadAllFfxTexturesInMemory(_ffxTexturesIEnumerable); }
private bool processBDT(BDT bdt, string baseDir, string subPath) { bool edited = false; foreach (BDT.File bdtEntry in bdt.Files) { if (stop) { return(false); } bool dcx = false; byte[] bdtEntryBytes = bdtEntry.Bytes; string bdtEntryExtension = Path.GetExtension(bdtEntry.Name); if (bdtEntryExtension == ".dcx") { dcx = true; bdtEntryBytes = DCX.Decompress(bdtEntryBytes); bdtEntryExtension = Path.GetExtension(bdtEntry.Name.Substring(0, bdtEntry.Name.Length - 4)); } if (bdtEntryExtension == ".tpf") { TPF tpf = TPF.Read(bdtEntryBytes); if (processTPF(tpf, baseDir, subPath)) { bdtEntry.Bytes = tpf.Write(); if (dcx) { bdtEntry.Bytes = DCX.Compress(bdtEntry.Bytes); } edited = true; } } // This whouldn't really be a problem, but I would like to know about it else { appendError("Error: {0}\r\n\u2514\u2500 Non-tpf found in tpfbdt: {1}", subPath, bdtEntry.Name); } } return(edited); }
public static void Repack(string sourceDir, string targetDir) { TPF tpf = new TPF(); XmlDocument xml = new XmlDocument(); xml.Load($"{sourceDir}\\_yabber-tpf.xml"); string filename = xml.SelectSingleNode("tpf/filename").InnerText; Enum.TryParse(xml.SelectSingleNode("tpf/compression")?.InnerText ?? "None", out tpf.Compression); tpf.Encoding = Convert.ToByte(xml.SelectSingleNode("tpf/encoding").InnerText, 16); tpf.Flag2 = Convert.ToByte(xml.SelectSingleNode("tpf/flag2").InnerText, 16); foreach (XmlNode texNode in xml.SelectNodes("tpf/textures/texture")) { string name = Path.GetFileNameWithoutExtension(texNode.SelectSingleNode("name").InnerText); byte format = Convert.ToByte(texNode.SelectSingleNode("format").InnerText); byte flags1 = Convert.ToByte(texNode.SelectSingleNode("flags1").InnerText, 16); TPF.FloatStruct floatStruct = null; XmlNode floatsNode = texNode.SelectSingleNode("FloatStruct"); if (floatsNode != null) { floatStruct = new TPF.FloatStruct(); floatStruct.Unk00 = int.Parse(floatsNode.Attributes["Unk00"].InnerText); foreach (XmlNode valueNode in floatsNode.SelectNodes("Value")) { floatStruct.Values.Add(float.Parse(valueNode.InnerText)); } } byte[] bytes = File.ReadAllBytes($"{sourceDir}\\{name}.dds"); var texture = new TPF.Texture(name, format, flags1, bytes); texture.FloatStruct = floatStruct; tpf.Textures.Add(texture); } string outPath = $"{targetDir}\\{filename}"; YBUtil.Backup(outPath); tpf.Write(outPath); }
/// <summary> /// Loads a loose virtual file /// </summary> /// <param name="virtualPath"></param> public void AddLoadFileTask(string virtualPath, AccessLevel al) { string bndout; var path = Locator.VirtualToRealPath(virtualPath, out bndout); IResourceHandle handle; if (path == null || virtualPath == "null") { return; } if (virtualPath.EndsWith(".hkx")) { handle = GetResource <HavokCollisionResource>(virtualPath); } else if (path.ToUpper().EndsWith(".TPF") || path.ToUpper().EndsWith(".TPF.DCX")) { string virt = virtualPath; if (virt.StartsWith($@"map/tex")) { var regex = new Regex(@"\d{4}$"); if (regex.IsMatch(virt)) { virt = virt.Substring(0, virt.Length - 5); } else if (virt.EndsWith("tex")) { virt = virt.Substring(0, virt.Length - 4); } } var ttask = new LoadTPFResourcesTask(virt, TPF.Read(path), al, Locator.Type); Tasks.Add(ttask); return; } else { handle = GetResource <FlverResource>(virtualPath); } var task = new LoadResourceFromFileTask(handle, path, al, Locator.Type); Tasks.Add(task); }