private static async Task SaveTextureTask(ICLIFlags flags, string path, SaveContext info, ulong textureGUID, string name = null) { bool convertTextures = true; string convertType = "tif"; string multiSurfaceConvertType = "tif"; bool createMultiSurfaceSheet = false; bool lossless = false; int maxMips = 1; if (flags is ExtractFlags extractFlags) { if (extractFlags.SkipTextures) { return; } createMultiSurfaceSheet = extractFlags.SheetMultiSurface; convertTextures = !extractFlags.RawTextures && !extractFlags.Raw; convertType = extractFlags.ConvertTexturesType.ToLowerInvariant(); lossless = extractFlags.ConvertTexturesLossless; multiSurfaceConvertType = convertType; if (extractFlags.ForceDDSMultiSurface) { multiSurfaceConvertType = "dds"; } if (convertType == "dds" && extractFlags.SaveMips) { maxMips = 0xF; } } if (!path.EndsWith(Path.DirectorySeparatorChar.ToString())) { path += Path.DirectorySeparatorChar; } FindLogic.Combo.TextureAsset textureInfo = info.m_info.m_textures[textureGUID]; string filePath = Path.Combine(path, name ?? $"{textureInfo.GetNameIndex()}"); if (teResourceGUID.Type(textureGUID) != 0x4) { filePath += $".{teResourceGUID.Type(textureGUID):X3}"; } if (Program.Flags != null && Program.Flags.Deduplicate) { if (ScratchDBInstance.HasRecord(textureGUID)) { return; } ScratchDBInstance[textureGUID] = new ScratchDB.ScratchPath($"{filePath}.{convertType}", true); } CreateDirectoryFromFile(path); await s_texurePrepareSemaphore.WaitAsync(); try { if (!convertTextures) { teTexture texture; using (Stream textureStream = OpenFile(textureGUID)) { texture = new teTexture(textureStream, true); textureStream.Position = 0; WriteFile(textureStream, $"{filePath}.004"); } if (!texture.PayloadRequired) { return; } for (int i = 0; i < texture.Payloads.Length; ++i) { using (Stream texturePayloadStream = OpenFile(texture.GetPayloadGUID(textureGUID, i))) WriteFile(texturePayloadStream, $"{filePath}_{i}.04D"); } } else { teTexture texture; using (Stream textureStream = OpenFile(textureGUID)) { if (textureStream == null) { return; } texture = new teTexture(textureStream); } //if (texture.Header.Flags.HasFlag(teTexture.Flags.CUBEMAP)) return; // for diffing when they add/regen loads of cubemaps if (texture.PayloadRequired) { for (int i = 0; i < texture.Payloads.Length; ++i) { using (var payloadStream = OpenFile(texture.GetPayloadGUID(textureGUID, i))) texture.LoadPayload(payloadStream, i); if (maxMips == 1) { break; } } } uint?width = null; uint?height = null; uint?surfaces = null; if (texture.Header.IsCubemap || texture.Header.IsArray || texture.HasMultipleSurfaces) { if (createMultiSurfaceSheet) { Logger.Debug("Combo", $"Saving {Path.GetFileName(filePath)} as a sheet because it has more than one surface"); height = (uint)(texture.Header.Height * texture.Header.Surfaces); surfaces = 1; texture.Header.Flags = 0; } else if (convertType != "tif" && convertType != "dds") { Logger.Debug("Combo", $"Saving {Path.GetFileName(filePath)} as {multiSurfaceConvertType} because it has more than one surface"); convertType = multiSurfaceConvertType; } } WICCodecs?imageFormat = null; switch (convertType) { case "tif": imageFormat = WICCodecs.TIFF; break; case "png": imageFormat = WICCodecs.PNG; break; case "jpg": imageFormat = WICCodecs.JPEG; break; } // if (convertType == "tga") imageFormat = Im.... oh // so there is no TGA image format. // sucks to be them if (convertType == "dds") { using (Stream convertedStream = texture.SaveToDDS(maxMips == 1 ? 1 : texture.Header.MipCount, width, height, surfaces)) { WriteFile(convertedStream, $"{filePath}.dds"); } return; } Process pProcess; using (Stream convertedStream = texture.SaveToDDS(maxMips == 1 ? 1 : texture.Header.MipCount, width, height, surfaces)) { var data = DDSConverter.ConvertDDS(convertedStream, DXGI_FORMAT.R8G8B8A8_UNORM, imageFormat.Value, 0); if (data != null) { WriteFile(data, $"{filePath}.{convertType}"); } else { convertedStream.Position = 0; WriteFile(convertedStream, $"{filePath}.dds"); Logger.Error("Combo", $"Unable to save {Path.GetFileName(filePath)} as {convertType} because DirectXTex failed."); } } } } finally { s_texurePrepareSemaphore.Release(); } }
public static void SaveTexture(ICLIFlags flags, string path, FindLogic.Combo.ComboInfo info, ulong textureGUID) { bool convertTextures = true; string convertType = "dds"; bool lossless = false; if (flags is ExtractFlags extractFlags) { convertTextures = extractFlags.ConvertTextures && !extractFlags.Raw; convertType = extractFlags.ConvertTexturesType.ToLowerInvariant(); lossless = extractFlags.ConvertTexturesLossless; if (extractFlags.SkipTextures) { return; } } path += Path.DirectorySeparatorChar; FindLogic.Combo.TextureInfoNew textureInfo = info.Textures[textureGUID]; string filePath = Path.Combine(path, $"{textureInfo.GetNameIndex()}"); if (Program.Flags.Deduplicate) { if (ScratchDBInstance.HasRecord(textureGUID)) { return; } ScratchDBInstance[textureGUID] = new ScratchDB.ScratchPath($"{filePath}.{convertType}"); } CreateDirectoryFromFile(path); if (!convertTextures) { using (Stream textureStream = OpenFile(textureInfo.GUID)) { teTexture texture = new teTexture(textureStream, true); textureStream.Position = 0; WriteFile(textureStream, $"{filePath}.004"); if (!texture.PayloadRequired) { return; } using (Stream texturePayloadStream = OpenFile(texture.GetPayloadGUID(textureGUID))) WriteFile(texturePayloadStream, $"{filePath}.04D"); } } else { using (Stream textureStream = OpenFile(textureGUID)) { if (textureStream == null) { return; } teTexture texture = new teTexture(textureStream); if (texture.PayloadRequired) { texture.LoadPayload(OpenFile(texture.GetPayloadGUID(textureGUID))); } using (Stream convertedStream = texture.SaveToDDS()) { convertedStream.Position = 0; if (convertType == "dds" || convertedStream.Length == 0) { WriteFile(convertedStream, $"{filePath}.dds"); return; } uint fourCC = texture.Header.GetFormat().ToPixelFormat().FourCC; bool isBcffValid = TextureConfig.DXGI_BC4.Contains((int)texture.Header.Format) || TextureConfig.DXGI_BC5.Contains((int)texture.Header.Format) || fourCC == TextureConfig.FOURCC_ATI1 || fourCC == TextureConfig.FOURCC_ATI2; ImageFormat imageFormat = null; if (convertType == "tif") { imageFormat = ImageFormat.Tiff; } // if (convertType == "tga") imageFormat = Im.... oh // so there is no TGA image format. // guess the TGA users are stuck with the DirectXTex stuff for now. if (isBcffValid && imageFormat != null) { BlockDecompressor decompressor = new BlockDecompressor(convertedStream); decompressor.CreateImage(); decompressor.Image.Save($"{filePath}.{convertType}", imageFormat); return; } string losslessFlag = lossless ? "-wiclossless" : string.Empty; Process pProcess = new Process { StartInfo = { FileName = "Third Party\\texconv.exe", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, CreateNoWindow = true, Arguments = $"-- \"{Path.GetFileName(filePath)}.dds\" -y -wicmulti {losslessFlag} -nologo -m 1 -ft {convertType} -f R8G8B8A8_UNORM -o \"{path}" }, EnableRaisingEvents = true }; // erm, so if you add an end quote to this then it breaks. // but start one on it's own is fine (we need something for "Winged Victory") pProcess.Start(); convertedStream.Position = 0; convertedStream.CopyTo(pProcess.StandardInput.BaseStream); pProcess.StandardInput.BaseStream.Close(); // pProcess.WaitForExit(); // not using this is kinda dangerous but I don't care // when texconv writes with to the console -nologo is has done/failed conversion string line = pProcess.StandardOutput.ReadLine(); if (line?.Contains("FAILED") == true) { convertedStream.Position = 0; WriteFile(convertedStream, $"{filePath}.dds"); } } } } }
public static void SaveTexture(ICLIFlags flags, string path, FindLogic.Combo.ComboInfo info, ulong textureGUID) { bool convertTextures = true; string convertType = "tif"; string multiSurfaceConvertType = "tif"; bool createMultiSurfaceSheet = false; bool lossless = false; int maxMips = 1; if (flags is ExtractFlags extractFlags) { if (extractFlags.SkipTextures) { return; } createMultiSurfaceSheet = extractFlags.SheetMultiSurface; convertTextures = !extractFlags.RawTextures && !extractFlags.Raw; convertType = extractFlags.ConvertTexturesType.ToLowerInvariant(); lossless = extractFlags.ConvertTexturesLossless; multiSurfaceConvertType = convertType; if (extractFlags.ForceDDSMultiSurface) { multiSurfaceConvertType = "dds"; } if (convertType == "dds" && extractFlags.SaveMips) { maxMips = 0xF; } } path += Path.DirectorySeparatorChar; FindLogic.Combo.TextureInfoNew textureInfo = info.Textures[textureGUID]; string filePath = Path.Combine(path, $"{textureInfo.GetNameIndex()}"); if (teResourceGUID.Type(textureGUID) != 0x4) { filePath += $".{teResourceGUID.Type(textureGUID):X3}"; } if (Program.Flags.Deduplicate) { if (ScratchDBInstance.HasRecord(textureGUID)) { return; } ScratchDBInstance[textureGUID] = new ScratchDB.ScratchPath($"{filePath}.{convertType}"); } CreateDirectoryFromFile(path); if (!convertTextures) { using (Stream textureStream = OpenFile(textureGUID)) { teTexture texture = new teTexture(textureStream, true); textureStream.Position = 0; WriteFile(textureStream, $"{filePath}.004"); if (!texture.PayloadRequired) { return; } for (int i = 0; i < texture.Payloads.Length; ++i) { using (Stream texturePayloadStream = OpenFile(texture.GetPayloadGUID(textureGUID, i))) WriteFile(texturePayloadStream, $"{filePath}_{i}.04D"); } } } else { using (Stream textureStream = OpenFile(textureGUID)) { if (textureStream == null) { return; } teTexture texture = new teTexture(textureStream); //if (texture.Header.Flags.HasFlag(teTexture.Flags.CUBEMAP)) return; // for diffing when they add/regen loads of cubemaps if (texture.PayloadRequired) { for (int i = 0; i < texture.Payloads.Length; ++i) { texture.LoadPayload(OpenFile(texture.GetPayloadGUID(textureGUID, i)), i); if (maxMips == 1) { break; } } } uint?width = null; uint?height = null; uint?surfaces = null; if (texture.Header.IsCubemap || texture.Header.IsArray || texture.HasMultipleSurfaces) { if (createMultiSurfaceSheet) { TankLib.Helpers.Logger.Debug("Combo", $"Saving {Path.GetFileName(filePath)} as a sheet because it has more than one surface"); height = (uint)(texture.Header.Height * texture.Header.Surfaces); surfaces = 1; texture.Header.Flags = 0; } else { TankLib.Helpers.Logger.Debug("Combo", $"Saving {Path.GetFileName(filePath)} as {multiSurfaceConvertType} because it has more than one surface"); convertType = multiSurfaceConvertType; } } using (Stream convertedStream = texture.SaveToDDS(maxMips == 1 ? 1 : texture.Header.MipCount, width, height, surfaces)) { convertedStream.Position = 0; if (convertType == "dds" || convertedStream.Length == 0) { WriteFile(convertedStream, $"{filePath}.dds"); return; } bool isBcffValid = teTexture.DXGI_BC4.Contains(texture.Header.Format) || teTexture.DXGI_BC5.Contains(texture.Header.Format) || teTexture.ATI2.Contains(texture.Header.GetTextureType()); ImageFormat imageFormat = null; if (convertType == "tif") { imageFormat = ImageFormat.Tiff; } if (convertType == "png") { imageFormat = ImageFormat.Png; } if (convertType == "jpg") { imageFormat = ImageFormat.Jpeg; } // if (convertType == "tga") imageFormat = Im.... oh // so there is no TGA image format. // guess the TGA users are stuck with the DirectXTex stuff for now. if (isBcffValid && imageFormat != null && !(texture.Header.IsCubemap || texture.Header.IsArray || texture.HasMultipleSurfaces)) { BlockDecompressor decompressor = new BlockDecompressor(convertedStream); decompressor.CreateImage(); decompressor.Image.Save($"{filePath}.{convertType}", imageFormat); return; } string losslessFlag = lossless ? "-wiclossless" : string.Empty; Process pProcess = new Process { StartInfo = { FileName = "Third Party\\texconv.exe", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, CreateNoWindow = true, Arguments = $"-- \"{Path.GetFileName(filePath)}.dds\" -y -wicmulti {losslessFlag} -nologo -m 1 -ft {convertType} -f R8G8B8A8_UNORM -o \"{(path.EndsWith(@"/") || path.EndsWith("\\") ? path.Substring(0, path.Length - 1) : path)}" }, EnableRaisingEvents = true }; pProcess.Start(); convertedStream.Position = 0; convertedStream.CopyTo(pProcess.StandardInput.BaseStream); pProcess.StandardInput.BaseStream.Flush(); pProcess.StandardInput.BaseStream.Close(); pProcess.WaitForExit(); // when texconv writes with to the console -nologo is has done/failed conversion string line = pProcess.StandardOutput.ReadToEnd(); if (line.Contains("FAILED")) { convertedStream.Position = 0; TankLib.Helpers.Logger.Debug("Combo", $"Saving {Path.GetFileName(filePath)} as dds because texconv failed."); WriteFile(convertedStream, $"{filePath}.dds"); } } } } }