private string processTextureMod(string filenameMod, int previewIndex, bool extract, bool replace, bool verify, string outDir, List <FoundTexture> textures, TexExplorer texExplorer, ref string log) { string errors = ""; if (filenameMod.EndsWith(".tpf", StringComparison.OrdinalIgnoreCase)) { if (!replace && !extract) { throw new Exception(); } int result; string fileName = ""; ulong dstLen = 0; string[] ddsList = null; ulong numEntries = 0; IntPtr handle = IntPtr.Zero; ZlibHelper.Zip zip = new ZlibHelper.Zip(); try { int indexTpf = -1; handle = zip.Open(filenameMod, ref numEntries, 1); for (ulong i = 0; i < numEntries; i++) { result = zip.GetCurrentFileInfo(handle, ref fileName, ref dstLen); fileName = fileName.Trim(); if (result != 0) { throw new Exception(); } if (Path.GetExtension(fileName).ToLowerInvariant() == ".def" || Path.GetExtension(fileName).ToLowerInvariant() == ".log") { indexTpf = (int)i; break; } result = zip.GoToNextFile(handle); if (result != 0) { throw new Exception(); } } byte[] listText = new byte[dstLen]; result = zip.ReadCurrentFile(handle, listText, dstLen); if (result != 0) { throw new Exception(); } ddsList = Encoding.ASCII.GetString(listText).Trim('\0').Replace("\r", "").TrimEnd('\n').Split('\n'); result = zip.GoToFirstFile(handle); if (result != 0) { throw new Exception(); } for (uint i = 0; i < numEntries; i++) { if (i == indexTpf) { result = zip.GoToNextFile(handle); continue; } try { uint crc = 0; result = zip.GetCurrentFileInfo(handle, ref fileName, ref dstLen); if (result != 0) { throw new Exception(); } fileName = fileName.Trim(); foreach (string dds in ddsList) { string ddsFile = dds.Split('|')[1]; if (ddsFile.ToLowerInvariant().Trim() != fileName.ToLowerInvariant()) { continue; } crc = uint.Parse(dds.Split('|')[0].Substring(2), System.Globalization.NumberStyles.HexNumber); break; } string filename = Path.GetFileName(fileName); if (crc == 0) { log += "Skipping file: " + filename + " not found in definition file, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; errors += "Skipping file: " + filename + " not found in definition file, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; zip.GoToNextFile(handle); continue; } int index = -1; for (int t = 0; t < textures.Count; t++) { if (textures[t].crc == crc) { index = t; break; } } if (index != -1) { FoundTexture foundTexture = textures[index]; byte[] data = new byte[dstLen]; result = zip.ReadCurrentFile(handle, data, dstLen); if (result != 0) { log += "Error in texture: " + foundTexture.name + string.Format("_0x{0:X8}", crc) + ", skipping texture, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; errors += "Error in texture: " + foundTexture.name + string.Format("_0x{0:X8}", crc) + ", skipping texture, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; zip.GoToNextFile(handle); continue; } if (texExplorer != null) { texExplorer._mainWindow.updateStatusLabel("Processing MOD " + Path.GetFileName(filenameMod) + " - File " + (i + 1) + " of " + numEntries + " - " + foundTexture.name); } if (extract) { string name = foundTexture.name + "_" + string.Format("0x{0:X8}", crc) + Path.GetExtension(fileName); using (FileStream output = new FileStream(Path.Combine(outDir, name), FileMode.Create, FileAccess.Write)) { output.Write(data, 0, (int)dstLen); } } else if (replace) { int indexPremap = -1; for (int t = 0; t < texExplorer.texturesPreMap.Count; t++) { if (texExplorer.texturesPreMap[t].crc == crc) { indexPremap = t; break; } } if (indexPremap != -1) { zip.GoToNextFile(handle); log += "Texture skipped. File " + filename + string.Format(" - 0x{0:X8}", crc) + " is not present in premap database - mod: " + filenameMod + Environment.NewLine; continue; } PixelFormat pixelFormat = texExplorer.texturesPreMap[indexPremap].pixfmt; Image image = new Image(data, Path.GetExtension(filename)); if (image.mipMaps[0].origWidth / image.mipMaps[0].origHeight != texExplorer.texturesPreMap[indexPremap].width / texExplorer.texturesPreMap[indexPremap].height) { zip.GoToNextFile(handle); errors += "Error in texture: " + foundTexture.name + string.Format("_0x{0:X8}", crc) + " This texture has wrong aspect ratio, skipping texture, entry: " + (i + 1) + " - mod: " + filename + Environment.NewLine; continue; } if (!image.checkDDSHaveAllMipmaps() || (foundTexture.list.Find(s => s.path != "").numMips > 1 && image.mipMaps.Count() <= 1) || (image.pixelFormat != pixelFormat)) { texExplorer._mainWindow.updateStatusLabel2("Converting/correcting texture: " + foundTexture.name); bool dxt1HasAlpha = false; byte dxt1Threshold = 128; if (texExplorer.texturesPreMap[indexPremap].flags == TexProperty.TextureTypes.OneBitAlpha) { dxt1HasAlpha = true; if (image.pixelFormat == PixelFormat.ARGB || image.pixelFormat == PixelFormat.DXT3 || image.pixelFormat == PixelFormat.DXT5) { errors += "Warning for texture: " + foundTexture.name + ". This texture converted from full alpha to binary alpha." + Environment.NewLine; } } image.correctMips(pixelFormat, dxt1HasAlpha, dxt1Threshold); } ModEntry entry = new ModEntry(); entry.cacheImage = image; entry.textureCrc = crc; entry.textureName = foundTexture.name; modsToReplace.Add(entry); } } else { log += "Texture skipped. File " + filename + string.Format(" - 0x{0:X8}", crc) + " is not present in your game setup - mod: " + filenameMod + Environment.NewLine; zip.GoToNextFile(handle); continue; } } catch { log += "Skipping not compatible content, entry: " + (i + 1) + " file: " + fileName + " - mod: " + filenameMod + Environment.NewLine; errors += "Skipping not compatible content, entry: " + (i + 1) + " file: " + fileName + " - mod: " + filenameMod + Environment.NewLine; } result = zip.GoToNextFile(handle); } zip.Close(handle); handle = IntPtr.Zero; } catch { log += "Mod is not compatible: " + filenameMod + Environment.NewLine; errors += "Mod is not compatible: " + filenameMod + Environment.NewLine; if (handle != IntPtr.Zero) { zip.Close(handle); } handle = IntPtr.Zero; } return(errors); } else if (filenameMod.EndsWith(".mod", StringComparison.OrdinalIgnoreCase)) { if (!replace && !extract) { throw new Exception(); } try { using (FileStream fs = new FileStream(filenameMod, FileMode.Open, FileAccess.Read)) { string package = ""; int len = fs.ReadInt32(); string version = fs.ReadStringASCIINull(); if (version.Length < 5) // legacy .mod { fs.SeekBegin(); } else { fs.SeekBegin(); len = fs.ReadInt32(); version = fs.ReadStringASCII(len); // version } uint numEntries = fs.ReadUInt32(); for (uint i = 0; i < numEntries; i++) { BinaryMod mod = new BinaryMod(); len = fs.ReadInt32(); string desc = fs.ReadStringASCII(len); // description len = fs.ReadInt32(); string scriptLegacy = fs.ReadStringASCII(len); string path = ""; if (desc.Contains("Binary Replacement")) { try { Misc.ParseME3xBinaryScriptMod(scriptLegacy, ref package, ref mod.exportId, ref path); if (mod.exportId == -1 || package == "" || path == "") { throw new Exception(); } } catch { len = fs.ReadInt32(); fs.Skip(len); errors += "Skipping not compatible content, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; continue; } mod.packagePath = Path.Combine(GameData.GamePath + path, package); len = fs.ReadInt32(); mod.data = fs.ReadToBuffer(len); if (!File.Exists(mod.packagePath)) { errors += "Warning: File " + mod.packagePath + " not exists in your game setup." + Environment.NewLine; log += "Warning: File " + mod.packagePath + " not exists in your game setup." + Environment.NewLine; continue; } if (replace) { ModEntry entry = new ModEntry(); entry.binaryModType = true; entry.packagePath = path; entry.exportId = mod.exportId; entry.binaryModData = mod.data; modsToReplace.Add(entry); } } else { string textureName = desc.Split(' ').Last(); FoundTexture f; int index = -1; try { index = Misc.ParseLegacyMe3xScriptMod(textures, scriptLegacy, textureName); if (index == -1) { throw new Exception(); } f = textures[index]; } catch { len = fs.ReadInt32(); fs.Skip(len); errors += "Skipping not compatible content, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; continue; } mod.textureCrc = f.crc; mod.textureName = f.name; len = fs.ReadInt32(); mod.data = fs.ReadToBuffer(len); if (replace) { int indexPremap = -1; try { indexPremap = Misc.ParseLegacyMe3xScriptMod(texExplorer.texturesPreMap, scriptLegacy, textureName); if (indexPremap == -1) { throw new Exception(); } } catch { len = fs.ReadInt32(); fs.Skip(len); errors += "Skipping not compatible content, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; continue; } PixelFormat pixelFormat = texExplorer.texturesPreMap[indexPremap].pixfmt; Image image = new Image(mod.data, Image.ImageFormat.DDS); if (image.mipMaps[0].origWidth / image.mipMaps[0].origHeight != texExplorer.texturesPreMap[indexPremap].width / texExplorer.texturesPreMap[indexPremap].height) { errors += "Error in texture: " + f.name + string.Format("_0x{0:X8}", f.crc) + " This texture has wrong aspect ratio, skipping texture, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; continue; } if (!image.checkDDSHaveAllMipmaps() || (f.list.Find(s => s.path != "").numMips > 1 && image.mipMaps.Count() <= 1) || (image.pixelFormat != pixelFormat)) { texExplorer._mainWindow.updateStatusLabel2("Converting/correcting texture: " + f.name); bool dxt1HasAlpha = false; byte dxt1Threshold = 128; if (texExplorer.texturesPreMap[indexPremap].flags == TexProperty.TextureTypes.OneBitAlpha) { dxt1HasAlpha = true; if (image.pixelFormat == PixelFormat.ARGB || image.pixelFormat == PixelFormat.DXT3 || image.pixelFormat == PixelFormat.DXT5) { errors += "Warning for texture: " + f.name + ". This texture converted from full alpha to binary alpha." + Environment.NewLine; } } image.correctMips(pixelFormat, dxt1HasAlpha, dxt1Threshold); } ModEntry entry = new ModEntry(); entry.cacheImage = image; entry.textureCrc = f.crc; entry.textureName = f.name; modsToReplace.Add(entry); } } } } } catch { errors += "Mod is not compatible: " + filenameMod + Environment.NewLine; } return(errors); } using (FileStream fs = new FileStream(filenameMod, FileMode.Open, FileAccess.Read)) { if (previewIndex == -1 && !extract && !replace) { texExplorer.listViewTextures.BeginUpdate(); } uint tag = fs.ReadUInt32(); uint version = fs.ReadUInt32(); if (tag != TreeScan.TextureModTag || version != TreeScan.TextureModVersion) { if (version != TreeScan.TextureModVersion) { errors += "File " + filenameMod + " was made with an older version of MEM, skipping..." + Environment.NewLine; log += "File " + filenameMod + " was made with an older version of MEM, skipping..." + Environment.NewLine; } else { errors += "File " + filenameMod + " is not a valid MEM mod, skipping..." + Environment.NewLine; log += "File " + filenameMod + " is not a valid MEM mod, skipping..." + Environment.NewLine; } if (previewIndex == -1 && !extract && !replace) { texExplorer.listViewTextures.EndUpdate(); } return(errors); } else { uint gameType = 0; fs.JumpTo(fs.ReadInt64()); gameType = fs.ReadUInt32(); if ((MeType)gameType != GameData.gameType) { errors += "File " + filenameMod + " is not a MEM mod valid for this game" + Environment.NewLine; log += "File " + filenameMod + " is not a MEM mod valid for this game" + Environment.NewLine; if (previewIndex == -1 && !extract && !replace) { texExplorer.listViewTextures.EndUpdate(); } return(errors); } } int numFiles = fs.ReadInt32(); List <FileMod> modFiles = new List <FileMod>(); for (int i = 0; i < numFiles; i++) { FileMod fileMod = new FileMod(); fileMod.tag = fs.ReadUInt32(); fileMod.name = fs.ReadStringASCIINull(); fileMod.offset = fs.ReadInt64(); fileMod.size = fs.ReadInt64(); modFiles.Add(fileMod); } numFiles = modFiles.Count; if (texExplorer != null && replace) { texExplorer._mainWindow.updateStatusLabel("Processing MOD " + Path.GetFileName(filenameMod)); } for (int i = 0; i < numFiles; i++) { string name = ""; uint crc = 0; long size = 0, dstLen = 0; int exportId = -1; string pkgPath = ""; byte[] dst = null; if (previewIndex != -1) { i = previewIndex; } fs.JumpTo(modFiles[i].offset); size = modFiles[i].size; if (modFiles[i].tag == FileTextureTag || modFiles[i].tag == FileTextureTag2) { name = fs.ReadStringASCIINull(); crc = fs.ReadUInt32(); } else if (modFiles[i].tag == FileBinaryTag) { name = modFiles[i].name; exportId = fs.ReadInt32(); pkgPath = fs.ReadStringASCIINull(); } else if (modFiles[i].tag == FileXdeltaTag) { name = modFiles[i].name; exportId = fs.ReadInt32(); pkgPath = fs.ReadStringASCIINull(); } if (texExplorer != null && (extract || replace)) { texExplorer._mainWindow.updateStatusLabel("Processing MOD " + Path.GetFileName(filenameMod) + " - File " + (i + 1) + " of " + numFiles + " - " + name); } if (previewIndex == -1 && !extract && !replace) { if (modFiles[i].tag == FileTextureTag || modFiles[i].tag == FileTextureTag2) { FoundTexture foundTexture; foundTexture = textures.Find(s => s.crc == crc); if (foundTexture.crc != 0) { ListViewItem item = new ListViewItem(foundTexture.name + " (" + Path.GetFileNameWithoutExtension(foundTexture.list.Find(s => s.path != "").path).ToUpperInvariant() + ")"); item.Name = i.ToString(); texExplorer.listViewTextures.Items.Add(item); } else { ListViewItem item = new ListViewItem(name + " (Texture not found: " + name + string.Format("_0x{0:X8}", crc) + ")"); item.Name = i.ToString(); texExplorer.listViewTextures.Items.Add(item); log += "Texture skipped. Texture " + name + string.Format("_0x{0:X8}", crc) + " is not present in your game setup" + Environment.NewLine; } } else if (modFiles[i].tag == FileBinaryTag) { ListViewItem item = new ListViewItem(name + " (Raw Binary Mod)"); item.Name = i.ToString(); texExplorer.listViewTextures.Items.Add(item); } else if (modFiles[i].tag == FileXdeltaTag) { ListViewItem item = new ListViewItem(name + " (Xdelta Binary Mod)"); item.Name = i.ToString(); texExplorer.listViewTextures.Items.Add(item); } else { ListViewItem item = new ListViewItem(name + " (Unknown)"); item.Name = i.ToString(); errors += "Unknown tag for file: " + name + Environment.NewLine; log += "Unknown tag for file: " + name + Environment.NewLine; } continue; } if (!replace || (modFiles[i].tag != FileTextureTag && modFiles[i].tag != FileTextureTag2)) { dst = decompressData(fs, size); dstLen = dst.Length; } if (extract) { if (modFiles[i].tag == FileTextureTag) { string filename = name + "_" + string.Format("0x{0:X8}", crc) + ".dds"; using (FileStream output = new FileStream(Path.Combine(outDir, Path.GetFileName(filename)), FileMode.Create, FileAccess.Write)) { output.Write(dst, 0, (int)dstLen); } } else if (modFiles[i].tag == FileTextureTag2) { string filename = name + "_" + string.Format("0x{0:X8}", crc) + "-memconvert.dds"; using (FileStream output = new FileStream(Path.Combine(outDir, Path.GetFileName(filename)), FileMode.Create, FileAccess.Write)) { output.Write(dst, 0, (int)dstLen); } } else if (modFiles[i].tag == FileBinaryTag) { string path = pkgPath; string newFilename; if (path.Contains("\\DLC\\")) { string dlcName = path.Split('\\')[3]; newFilename = "D" + dlcName.Length + "-" + dlcName + "-"; } else { newFilename = "B"; } newFilename += Path.GetFileName(path).Length + "-" + Path.GetFileName(path) + "-E" + exportId + ".bin"; using (FileStream output = new FileStream(Path.Combine(outDir, newFilename), FileMode.Create, FileAccess.Write)) { output.Write(dst, 0, (int)dstLen); } } else if (modFiles[i].tag == FileXdeltaTag) { string path = pkgPath; string newFilename; if (path.Contains("\\DLC\\")) { string dlcName = path.Split('\\')[3]; newFilename = "D" + dlcName.Length + "-" + dlcName + "-"; } else { newFilename = "B"; } newFilename += Path.GetFileName(path).Length + "-" + Path.GetFileName(path) + "-E" + exportId + ".xdelta"; using (FileStream output = new FileStream(Path.Combine(outDir, newFilename), FileMode.Create, FileAccess.Write)) { output.Write(dst, 0, (int)dstLen); } } else { errors += "Unknown tag for file: " + name + Environment.NewLine; log += "Unknown tag for file: " + name + Environment.NewLine; } continue; } if (previewIndex != -1) { if (modFiles[i].tag == FileTextureTag || modFiles[i].tag == FileTextureTag2) { Image image = new Image(dst, Image.ImageFormat.DDS); texExplorer.pictureBoxPreview.Image = image.getBitmapARGB(); } else { texExplorer.pictureBoxPreview.Image = null; } break; } else if (replace) { if (modFiles[i].tag == FileTextureTag || modFiles[i].tag == FileTextureTag2) { int index = -1; for (int t = 0; t < textures.Count; t++) { if (textures[t].crc == crc) { index = t; break; } } if (index != -1) { FoundTexture foundTexture = textures[index]; ModEntry entry = new ModEntry(); entry.textureCrc = foundTexture.crc; entry.textureName = foundTexture.name; if (modFiles[i].tag == FileTextureTag2) { entry.markConvert = true; } entry.memPath = filenameMod; entry.memEntryOffset = fs.Position; entry.memEntrySize = size; modsToReplace.Add(entry); } else { log += "Error: Texture " + name + string.Format("_0x{0:X8}", crc) + "is not present in your game setup. Texture skipped." + Environment.NewLine; } } else if (modFiles[i].tag == FileBinaryTag) { string path = GameData.GamePath + pkgPath; if (!File.Exists(path)) { errors += "Warning: File " + path + " not exists in your game setup." + Environment.NewLine; log += "Warning: File " + path + " not exists in your game setup." + Environment.NewLine; continue; } ModEntry entry = new ModEntry(); entry.binaryModType = true; entry.packagePath = pkgPath; entry.exportId = exportId; entry.binaryModData = dst; modsToReplace.Add(entry); } else if (modFiles[i].tag == FileXdeltaTag) { string path = GameData.GamePath + pkgPath; if (!File.Exists(path)) { errors += "Warning: File " + path + " not exists in your game setup." + Environment.NewLine; log += "Warning: File " + path + " not exists in your game setup." + Environment.NewLine; continue; } ModEntry entry = new ModEntry(); Package pkg = new Package(path); byte[] buffer = new Xdelta3Helper.Xdelta3().Decompress(pkg.getExportData(exportId), dst); if (buffer.Length == 0) { errors += "Warning: Xdelta patch for " + path + " failed to apply." + Environment.NewLine; log += "Warning: Xdelta patch for " + path + " failed to apply." + Environment.NewLine; continue; } entry.binaryModType = true; entry.packagePath = pkgPath; entry.exportId = exportId; entry.binaryModData = buffer; modsToReplace.Add(entry); pkg.Dispose(); } else { errors += "Error: Unknown tag for file: " + name + Environment.NewLine; log += "Error: Unknown tag for file: " + name + Environment.NewLine; } } else { throw new Exception(); } } if (previewIndex == -1 && !extract && !replace) { texExplorer.listViewTextures.EndUpdate(); } } return(errors); }
static private string convertDataModtoMem(string inputDir, string memFilePath) { string errors = ""; string[] files = null; Console.WriteLine("Mods conversion started..."); List <string> list = Directory.GetFiles(inputDir, "*.tpf").Where(item => item.EndsWith(".tpf", StringComparison.OrdinalIgnoreCase)).ToList(); list.AddRange(Directory.GetFiles(inputDir, "*.mod").Where(item => item.EndsWith(".mod", StringComparison.OrdinalIgnoreCase))); list.AddRange(Directory.GetFiles(inputDir, "*.dds").Where(item => item.EndsWith(".dds", StringComparison.OrdinalIgnoreCase))); list.Sort(); files = list.ToArray(); int result; string fileName = ""; uint dstLen = 0; string[] ddsList = null; ulong numEntries = 0; List <TexExplorer.BinaryMod> mods = new List <TexExplorer.BinaryMod>(); foreach (string file in files) { if (file.EndsWith(".mod", StringComparison.OrdinalIgnoreCase)) { try { using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) { string package = ""; int len = fs.ReadInt32(); string version = fs.ReadStringASCII(len); // version if (version.Length < 5) // legacy .mod { fs.SeekBegin(); } numEntries = fs.ReadUInt32(); for (uint i = 0; i < numEntries; i++) { TexExplorer.BinaryMod mod = new TexExplorer.BinaryMod(); len = fs.ReadInt32(); string desc = fs.ReadStringASCII(len); // description len = fs.ReadInt32(); string scriptLegacy = fs.ReadStringASCII(len); string path = ""; if (desc.Contains("Binary Replacement")) { try { Misc.ParseME3xBinaryScriptMod(scriptLegacy, ref package, ref mod.exportId, ref path); if (mod.exportId == -1 || package == "" || path == "") { throw new Exception(); } } catch { len = fs.ReadInt32(); fs.Skip(len); errors += "Skipping not compatible content, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; continue; } mod.packagePath = GameData.RelativeGameData(Path.Combine(path, package)); mod.binaryMod = true; len = fs.ReadInt32(); mod.data = fs.ReadToBuffer(len); } else { string textureName = desc.Split(' ').Last(); FoundTexture f; try { f = Misc.ParseLegacyMe3xScriptMod(textures, scriptLegacy, textureName); mod.textureCrc = f.crc; if (mod.textureCrc == 0) { throw new Exception(); } } catch { len = fs.ReadInt32(); fs.Skip(len); errors += "Skipping not compatible content, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; continue; } textureName = f.name; mod.textureName = textureName; mod.binaryMod = false; len = fs.ReadInt32(); mod.data = fs.ReadToBuffer(len); DDSImage image = new DDSImage(new MemoryStream(mod.data)); if (!image.checkExistAllMipmaps()) { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", f.crc) + " This texture has not all the required mipmaps, skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; continue; } Package pkg = new Package(GameData.GamePath + f.list[0].path); Texture texture = new Texture(pkg, f.list[0].exportID, pkg.getExportData(f.list[0].exportID)); if (texture.mipMapsList.Count > 1 && image.mipMaps.Count() <= 1) { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", f.crc) + " This texture must have mipmaps, skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; continue; } string fmt = texture.properties.getProperty("Format").valueName; DDSFormat ddsFormat = DDSImage.convertFormat(fmt); if (image.ddsFormat != ddsFormat) { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", f.crc) + " This texture has wrong texture format, should be: " + ddsFormat + ", skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; continue; } if (image.mipMaps[0].origWidth / image.mipMaps[0].origHeight != texture.mipMapsList[0].width / texture.mipMapsList[0].height) { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", f.crc) + " This texture has wrong aspect ratio, skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; continue; } } mods.Add(mod); } } } catch { errors += "Mod is not compatible: " + file + Environment.NewLine; continue; } } else if (file.EndsWith(".tpf", StringComparison.OrdinalIgnoreCase)) { IntPtr handle = IntPtr.Zero; try { byte[] buffer = File.ReadAllBytes(file); handle = ZlibHelper.Zip.Open(buffer, ref numEntries, 1); if (ZlibHelper.Zip.LocateFile(handle, "texmod.def") != 0) { throw new Exception(); } result = ZlibHelper.Zip.GetCurrentFileInfo(handle, ref fileName, ref dstLen); if (result != 0) { throw new Exception(); } byte[] listText = new byte[dstLen]; result = ZlibHelper.Zip.ReadCurrentFile(handle, listText, dstLen); if (result != 0) { throw new Exception(); } ddsList = Encoding.ASCII.GetString(listText).Trim('\0').Replace("\r", "").TrimEnd('\n').Split('\n'); result = ZlibHelper.Zip.GoToFirstFile(handle); if (result != 0) { throw new Exception(); } for (uint i = 0; i < numEntries; i++) { TexExplorer.BinaryMod mod = new TexExplorer.BinaryMod(); try { uint crc = 0; result = ZlibHelper.Zip.GetCurrentFileInfo(handle, ref fileName, ref dstLen); if (result != 0) { throw new Exception(); } string filename = Path.GetFileName(fileName); foreach (string dds in ddsList) { string ddsFile = dds.Split('|')[1]; if (ddsFile.ToLowerInvariant() != filename.ToLowerInvariant()) { continue; } crc = uint.Parse(dds.Split('|')[0].Substring(2), System.Globalization.NumberStyles.HexNumber); break; } if (crc == 0) { if (filename != "texmod.def") { errors += "Skipping file: " + filename + " not founded in texmod.def, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; } ZlibHelper.Zip.GoToNextFile(handle); continue; } List <FoundTexture> foundCrcList = textures.FindAll(s => s.crc == crc); if (foundCrcList.Count == 0) { errors += "Texture skipped. File " + filename + string.Format(" - 0x{0:X8}", crc) + " is not present in your game setup - mod: " + file + Environment.NewLine; ZlibHelper.Zip.GoToNextFile(handle); continue; } string textureName = foundCrcList[0].name; mod.textureName = textureName; mod.binaryMod = false; mod.textureCrc = crc; mod.data = new byte[dstLen]; result = ZlibHelper.Zip.ReadCurrentFile(handle, mod.data, dstLen); if (result != 0) { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", crc) + ", skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; ZlibHelper.Zip.GoToNextFile(handle); continue; } uint tag = BitConverter.ToUInt32(mod.data, 0); if (tag != 0x20534444) // DDS { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", crc) + " This texture is not in DDS format, skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; ZlibHelper.Zip.GoToNextFile(handle); continue; } DDSImage image = new DDSImage(new MemoryStream(mod.data)); if (!image.checkExistAllMipmaps()) { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", crc) + " This texture has not all the required mipmaps, skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; ZlibHelper.Zip.GoToNextFile(handle); continue; } Package pkg = new Package(GameData.GamePath + foundCrcList[0].list[0].path); Texture texture = new Texture(pkg, foundCrcList[0].list[0].exportID, pkg.getExportData(foundCrcList[0].list[0].exportID)); if (texture.mipMapsList.Count > 1 && image.mipMaps.Count() <= 1) { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", crc) + " This texture must have mipmaps, skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; ZlibHelper.Zip.GoToNextFile(handle); continue; } string fmt = texture.properties.getProperty("Format").valueName; DDSFormat ddsFormat = DDSImage.convertFormat(fmt); if (image.ddsFormat != ddsFormat) { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", crc) + " This texture has wrong texture format, should be: " + ddsFormat + ", skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; ZlibHelper.Zip.GoToNextFile(handle); continue; } if (image.mipMaps[0].origWidth / image.mipMaps[0].origHeight != texture.mipMapsList[0].width / texture.mipMapsList[0].height) { errors += "Error in texture: " + textureName + string.Format("_0x{0:X8}", crc) + " This texture has wrong aspect ratio, skipping texture, entry: " + (i + 1) + " - mod: " + file + Environment.NewLine; ZlibHelper.Zip.GoToNextFile(handle); continue; } mods.Add(mod); } catch { errors += "Skipping not compatible content, entry: " + (i + 1) + " file: " + fileName + " - mod: " + file + Environment.NewLine; } ZlibHelper.Zip.GoToNextFile(handle); } ZlibHelper.Zip.Close(handle); handle = IntPtr.Zero; } catch { errors += "Mod is not compatible: " + file + Environment.NewLine; if (handle != IntPtr.Zero) { ZlibHelper.Zip.Close(handle); } handle = IntPtr.Zero; continue; } } else if (file.EndsWith(".dds", StringComparison.OrdinalIgnoreCase)) { TexExplorer.BinaryMod mod = new TexExplorer.BinaryMod(); string filename = Path.GetFileNameWithoutExtension(file).ToLowerInvariant(); if (!filename.Contains("0x")) { errors += "Texture filename not valid: " + Path.GetFileName(file) + " Texture filename must include texture CRC (0xhhhhhhhh). Skipping texture..." + Environment.NewLine; continue; } int idx = filename.IndexOf("0x"); if (filename.Length - idx < 10) { errors += "Texture filename not valid: " + Path.GetFileName(file) + " Texture filename must include texture CRC (0xhhhhhhhh). Skipping texture..." + Environment.NewLine; continue; } uint crc; string crcStr = filename.Substring(idx + 2, 8); try { crc = uint.Parse(crcStr, System.Globalization.NumberStyles.HexNumber); } catch { errors += "Texture filename not valid: " + Path.GetFileName(file) + " Texture filename must include texture CRC (0xhhhhhhhh). Skipping texture..." + Environment.NewLine; continue; } List <FoundTexture> foundCrcList = textures.FindAll(s => s.crc == crc); if (foundCrcList.Count == 0) { errors += "Texture skipped. Texture " + Path.GetFileName(file) + " is not present in your game setup." + Environment.NewLine; continue; } using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) { mod.data = fs.ReadToBuffer((int)fs.Length); DDSImage image = new DDSImage(new MemoryStream(mod.data)); if (!image.checkExistAllMipmaps()) { errors += "Error in texture: " + Path.GetFileName(file) + " This texture has not all the required mipmaps, skipping texture..." + Environment.NewLine; continue; } Package pkg = new Package(GameData.GamePath + foundCrcList[0].list[0].path); Texture texture = new Texture(pkg, foundCrcList[0].list[0].exportID, pkg.getExportData(foundCrcList[0].list[0].exportID)); if (texture.mipMapsList.Count > 1 && image.mipMaps.Count() <= 1) { errors += "Error in texture: " + Path.GetFileName(file) + " This texture must have mipmaps, skipping texture..." + Environment.NewLine; continue; } string fmt = texture.properties.getProperty("Format").valueName; DDSFormat ddsFormat = DDSImage.convertFormat(fmt); if (image.ddsFormat != ddsFormat) { errors += "Error in texture: " + Path.GetFileName(file) + " This texture has wrong texture format, should be: " + ddsFormat + ", skipping texture..." + Environment.NewLine; continue; } if (image.mipMaps[0].origWidth / image.mipMaps[0].origHeight != texture.mipMapsList[0].width / texture.mipMapsList[0].height) { errors += "Error in texture: " + Path.GetFileName(file) + " This texture has wrong aspect ratio, skipping texture..." + Environment.NewLine; continue; } mod.textureName = foundCrcList[0].name; mod.binaryMod = false; mod.textureCrc = crc; mods.Add(mod); } } } if (mods.Count == 0) { Console.WriteLine("Mods conversion failed"); return(errors); } Console.WriteLine("Saving to MEM file... " + memFilePath); List <MipMaps.FileMod> modFiles = new List <MipMaps.FileMod>(); FileStream outFs; if (File.Exists(memFilePath)) { File.Delete(memFilePath); } using (outFs = new FileStream(memFilePath, FileMode.Create, FileAccess.Write)) { outFs.WriteUInt32(TexExplorer.TextureModTag); outFs.WriteUInt32(TexExplorer.TextureModVersion); outFs.WriteInt64(0); // filled later for (int l = 0; l < mods.Count; l++) { MipMaps.FileMod fileMod = new MipMaps.FileMod(); Stream dst = MipMaps.compressData(mods[l].data); dst.SeekBegin(); fileMod.offset = outFs.Position; fileMod.size = dst.Length; if (mods[l].binaryMod) { fileMod.tag = MipMaps.FileBinaryTag; fileMod.name = Path.GetFileNameWithoutExtension(mods[l].packagePath) + "_" + mods[l].exportId + ".bin"; outFs.WriteInt32(mods[l].exportId); outFs.WriteStringASCIINull(mods[l].packagePath); } else { fileMod.tag = MipMaps.FileTextureTag; fileMod.name = mods[l].textureName + string.Format("_0x{0:X8}", mods[l].textureCrc) + ".dds"; outFs.WriteStringASCIINull(mods[l].textureName); outFs.WriteUInt32(mods[l].textureCrc); } outFs.WriteFromStream(dst, dst.Length); modFiles.Add(fileMod); } long pos = outFs.Position; outFs.SeekBegin(); outFs.WriteUInt32(TexExplorer.TextureModTag); outFs.WriteUInt32(TexExplorer.TextureModVersion); outFs.WriteInt64(pos); outFs.JumpTo(pos); outFs.WriteUInt32((uint)GameData.gameType); outFs.WriteInt32(modFiles.Count); for (int i = 0; i < modFiles.Count; i++) { outFs.WriteUInt32(modFiles[i].tag); outFs.WriteStringASCIINull(modFiles[i].name); outFs.WriteInt64(modFiles[i].offset); outFs.WriteInt64(modFiles[i].size); } } Console.WriteLine("Mods conversion process completed"); return(errors); }
private string processTextureMod(string filenameMod, int previewIndex, bool extract, bool replace, bool verify, string outDir, List <FoundTexture> textures, CachePackageMgr cachePackageMgr, TexExplorer texExplorer, ref string log) { string errors = ""; if (filenameMod.EndsWith(".tpf", StringComparison.OrdinalIgnoreCase)) { if (!replace && !extract) { throw new Exception(); } int result; string fileName = ""; ulong dstLen = 0; string[] ddsList = null; ulong numEntries = 0; IntPtr handle = IntPtr.Zero; ZlibHelper.Zip zip = new ZlibHelper.Zip(); try { int indexTpf = -1; handle = zip.Open(filenameMod, ref numEntries, 1); for (ulong i = 0; i < numEntries; i++) { result = zip.GetCurrentFileInfo(handle, ref fileName, ref dstLen); fileName = fileName.Trim(); if (result != 0) { throw new Exception(); } if (Path.GetExtension(fileName).ToLowerInvariant() == ".def" || Path.GetExtension(fileName).ToLowerInvariant() == ".log") { indexTpf = (int)i; break; } result = zip.GoToNextFile(handle); if (result != 0) { throw new Exception(); } } byte[] listText = new byte[dstLen]; result = zip.ReadCurrentFile(handle, listText, dstLen); if (result != 0) { throw new Exception(); } ddsList = Encoding.ASCII.GetString(listText).Trim('\0').Replace("\r", "").TrimEnd('\n').Split('\n'); result = zip.GoToFirstFile(handle); if (result != 0) { throw new Exception(); } for (uint i = 0; i < numEntries; i++) { if (i == indexTpf) { result = zip.GoToNextFile(handle); continue; } try { uint crc = 0; result = zip.GetCurrentFileInfo(handle, ref fileName, ref dstLen); if (result != 0) { throw new Exception(); } fileName = fileName.Trim(); foreach (string dds in ddsList) { string ddsFile = dds.Split('|')[1]; if (ddsFile.ToLowerInvariant().Trim() != fileName.ToLowerInvariant()) { continue; } crc = uint.Parse(dds.Split('|')[0].Substring(2), System.Globalization.NumberStyles.HexNumber); break; } string filename = Path.GetFileName(fileName); if (crc == 0) { log += "Skipping file: " + filename + " not found in definition file, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; errors += "Skipping file: " + filename + " not found in definition file, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; zip.GoToNextFile(handle); continue; } FoundTexture foundTexture = textures.Find(s => s.crc == crc); if (foundTexture.crc != 0) { byte[] data = new byte[dstLen]; result = zip.ReadCurrentFile(handle, data, dstLen); if (result != 0) { log += "Error in texture: " + foundTexture.name + string.Format("_0x{0:X8}", crc) + ", skipping texture, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; errors += "Error in texture: " + foundTexture.name + string.Format("_0x{0:X8}", crc) + ", skipping texture, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; zip.GoToNextFile(handle); continue; } if (texExplorer != null) { texExplorer._mainWindow.updateStatusLabel("Processing MOD " + Path.GetFileName(filenameMod) + " - File " + (i + 1) + " of " + numEntries + " - " + foundTexture.name); } if (extract) { string name = foundTexture.name + "_" + string.Format("0x{0:X8}", crc) + Path.GetExtension(fileName); using (FileStream output = new FileStream(Path.Combine(outDir, name), FileMode.Create, FileAccess.Write)) { output.Write(data, 0, (int)dstLen); } } else { Image image = new Image(data, Path.GetExtension(filename)); errors += replaceTexture(image, foundTexture.list, cachePackageMgr, foundTexture.name, crc, verify); } } else { log += "Texture skipped. File " + filename + string.Format(" - 0x{0:X8}", crc) + " is not present in your game setup - mod: " + filenameMod + Environment.NewLine; zip.GoToNextFile(handle); continue; } } catch { log += "Skipping not compatible content, entry: " + (i + 1) + " file: " + fileName + " - mod: " + filenameMod + Environment.NewLine; errors += "Skipping not compatible content, entry: " + (i + 1) + " file: " + fileName + " - mod: " + filenameMod + Environment.NewLine; } result = zip.GoToNextFile(handle); } zip.Close(handle); handle = IntPtr.Zero; } catch { log += "Mod is not compatible: " + filenameMod + Environment.NewLine; errors += "Mod is not compatible: " + filenameMod + Environment.NewLine; if (handle != IntPtr.Zero) { zip.Close(handle); } handle = IntPtr.Zero; } return(errors); } else if (filenameMod.EndsWith(".mod", StringComparison.OrdinalIgnoreCase)) { if (!replace && !extract) { throw new Exception(); } try { using (FileStream fs = new FileStream(filenameMod, FileMode.Open, FileAccess.Read)) { string package = ""; int len = fs.ReadInt32(); string version = fs.ReadStringASCIINull(); if (version.Length < 5) // legacy .mod { fs.SeekBegin(); } else { fs.SeekBegin(); len = fs.ReadInt32(); version = fs.ReadStringASCII(len); // version } uint numEntries = fs.ReadUInt32(); for (uint i = 0; i < numEntries; i++) { TexExplorer.BinaryMod mod = new TexExplorer.BinaryMod(); len = fs.ReadInt32(); string desc = fs.ReadStringASCII(len); // description len = fs.ReadInt32(); string scriptLegacy = fs.ReadStringASCII(len); string path = ""; if (desc.Contains("Binary Replacement")) { try { Misc.ParseME3xBinaryScriptMod(scriptLegacy, ref package, ref mod.exportId, ref path); if (mod.exportId == -1 || package == "" || path == "") { throw new Exception(); } } catch { len = fs.ReadInt32(); fs.Skip(len); errors += "Skipping not compatible content, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; continue; } mod.packagePath = Path.Combine(GameData.GamePath + path, package); len = fs.ReadInt32(); mod.data = fs.ReadToBuffer(len); if (!File.Exists(mod.packagePath)) { errors += "Warning: File " + mod.packagePath + " not exists in your game setup." + Environment.NewLine; log += "Warning: File " + mod.packagePath + " not exists in your game setup." + Environment.NewLine; continue; } Package pkg = cachePackageMgr.OpenPackage(mod.packagePath); pkg.setExportData(mod.exportId, mod.data); } else { string textureName = desc.Split(' ').Last(); FoundTexture f; try { f = Misc.ParseLegacyMe3xScriptMod(textures, scriptLegacy, textureName); mod.textureCrc = f.crc; if (mod.textureCrc == 0) { throw new Exception(); } } catch { len = fs.ReadInt32(); fs.Skip(len); errors += "Skipping not compatible content, entry: " + (i + 1) + " - mod: " + filenameMod + Environment.NewLine; continue; } textureName = f.name; mod.textureName = textureName; len = fs.ReadInt32(); mod.data = fs.ReadToBuffer(len); PixelFormat pixelFormat = f.pixfmt; Image image = new Image(mod.data, Image.ImageFormat.DDS); errors += replaceTexture(image, f.list, cachePackageMgr, f.name, f.crc, verify); } } } } catch { errors += "Mod is not compatible: " + filenameMod + Environment.NewLine; } return(errors); } using (FileStream fs = new FileStream(filenameMod, FileMode.Open, FileAccess.Read)) { if (previewIndex == -1 && !extract && !replace) { texExplorer.listViewTextures.BeginUpdate(); } uint tag = fs.ReadUInt32(); uint version = fs.ReadUInt32(); if (tag != TexExplorer.TextureModTag || version != TexExplorer.TextureModVersion) { if (version != TexExplorer.TextureModVersion) { errors += "File " + filenameMod + " was made with an older version of MEM, skipping..." + Environment.NewLine; log += "File " + filenameMod + " was made with an older version of MEM, skipping..." + Environment.NewLine; } else { errors += "File " + filenameMod + " is not a valid MEM mod, skipping..." + Environment.NewLine; log += "File " + filenameMod + " is not a valid MEM mod, skipping..." + Environment.NewLine; } if (previewIndex == -1 && !extract && !replace) { texExplorer.listViewTextures.EndUpdate(); } return(errors); } else { uint gameType = 0; fs.JumpTo(fs.ReadInt64()); gameType = fs.ReadUInt32(); if (textures != null && (MeType)gameType != GameData.gameType) { errors += "File " + filenameMod + " is not a MEM mod valid for this game" + Environment.NewLine; log += "File " + filenameMod + " is not a MEM mod valid for this game" + Environment.NewLine; if (previewIndex == -1 && !extract && !replace) { texExplorer.listViewTextures.EndUpdate(); } return(errors); } } int numFiles = fs.ReadInt32(); List <FileMod> modFiles = new List <FileMod>(); for (int i = 0; i < numFiles; i++) { FileMod fileMod = new FileMod(); fileMod.tag = fs.ReadUInt32(); fileMod.name = fs.ReadStringASCIINull(); fileMod.offset = fs.ReadInt64(); fileMod.size = fs.ReadInt64(); modFiles.Add(fileMod); } numFiles = modFiles.Count; for (int i = 0; i < numFiles; i++) { string name = ""; uint crc = 0; long size = 0, dstLen = 0; int exportId = -1; string pkgPath = ""; byte[] dst = null; if (previewIndex != -1) { i = previewIndex; } fs.JumpTo(modFiles[i].offset); size = modFiles[i].size; if (modFiles[i].tag == FileTextureTag) { name = fs.ReadStringASCIINull(); crc = fs.ReadUInt32(); } else if (modFiles[i].tag == FileBinaryTag) { name = modFiles[i].name; exportId = fs.ReadInt32(); pkgPath = fs.ReadStringASCIINull(); } if (texExplorer != null) { texExplorer._mainWindow.updateStatusLabel("Processing MOD " + Path.GetFileName(filenameMod) + " - File " + (i + 1) + " of " + numFiles + " - " + name); } if (previewIndex == -1 && !extract && !replace) { if (modFiles[i].tag == FileTextureTag) { FoundTexture foundTexture; foundTexture = textures.Find(s => s.crc == crc); if (foundTexture.crc != 0) { ListViewItem item = new ListViewItem(foundTexture.name + " (" + Path.GetFileNameWithoutExtension(foundTexture.list[0].path).ToUpperInvariant() + ")"); item.Name = i.ToString(); texExplorer.listViewTextures.Items.Add(item); } else { ListViewItem item = new ListViewItem(name + " (Texture not found: " + name + string.Format("_0x{0:X8}", crc) + ")"); item.Name = i.ToString(); texExplorer.listViewTextures.Items.Add(item); log += "Texture skipped. Texture " + name + string.Format("_0x{0:X8}", crc) + " is not present in your game setup" + Environment.NewLine; } } else if (modFiles[i].tag == FileBinaryTag) { ListViewItem item = new ListViewItem(name + " (Binary Mod)"); item.Name = i.ToString(); texExplorer.listViewTextures.Items.Add(item); } else { ListViewItem item = new ListViewItem(name + " (Unknown)"); item.Name = i.ToString(); errors += "Unknown tag for file: " + name + Environment.NewLine; log += "Unknown tag for file: " + name + Environment.NewLine; } continue; } dst = decompressData(fs, size); dstLen = dst.Length; if (extract) { if (modFiles[i].tag == FileTextureTag) { string filename = name + "_" + string.Format("0x{0:X8}", crc) + ".dds"; using (FileStream output = new FileStream(Path.Combine(outDir, Path.GetFileName(filename)), FileMode.Create, FileAccess.Write)) { output.Write(dst, 0, (int)dstLen); } } else if (modFiles[i].tag == FileBinaryTag) { string path = pkgPath; string newFilename; if (path.Contains("\\DLC\\")) { string dlcName = path.Split('\\')[3]; newFilename = "D" + dlcName.Length + "-" + dlcName + "-"; } else { newFilename = "B"; } newFilename += Path.GetFileName(path).Length + "-" + Path.GetFileName(path) + "-E" + exportId + ".bin"; using (FileStream output = new FileStream(Path.Combine(outDir, newFilename), FileMode.Create, FileAccess.Write)) { output.Write(dst, 0, (int)dstLen); } } else { errors += "Unknown tag for file: " + name + Environment.NewLine; log += "Unknown tag for file: " + name + Environment.NewLine; } continue; } if (previewIndex != -1) { if (modFiles[i].tag == FileTextureTag) { Image image = new Image(dst, Image.ImageFormat.DDS); texExplorer.pictureBoxPreview.Image = image.getBitmapARGB(); } else { texExplorer.pictureBoxPreview.Image = null; } break; } else if (replace) { if (modFiles[i].tag == FileTextureTag) { FoundTexture foundTexture; foundTexture = textures.Find(s => s.crc == crc); if (foundTexture.crc != 0) { Image image = new Image(dst, Image.ImageFormat.DDS); errors += replaceTexture(image, foundTexture.list, cachePackageMgr, foundTexture.name, crc, verify); } else { log += "Error: Texture " + name + string.Format("_0x{0:X8}", crc) + "is not present in your game setup. Texture skipped." + Environment.NewLine; } } else if (modFiles[i].tag == FileBinaryTag) { string path = GameData.GamePath + pkgPath; if (!File.Exists(path)) { errors += "Warning: File " + path + " not exists in your game setup." + Environment.NewLine; log += "Warning: File " + path + " not exists in your game setup." + Environment.NewLine; continue; } Package pkg = cachePackageMgr.OpenPackage(path); pkg.setExportData(exportId, dst); } else { errors += "Error: Unknown tag for file: " + name + Environment.NewLine; log += "Error: Unknown tag for file: " + name + Environment.NewLine; } } else { throw new Exception(); } } if (previewIndex == -1 && !extract && !replace) { texExplorer.listViewTextures.EndUpdate(); } } return(errors); }