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); }