/// <summary> /// Gets the redengine texture format from the compression method /// Used when creating a dds from an xbm /// TODO: TEST THIS!!! /// </summary> /// <param name="compression"></param> /// <returns></returns> public static EFormat GetEFormatFromCompression(ETextureCompression compression) { switch (compression) { // missing: 0xFD // missing: 0x0 //EFormat.R8G8B8A8_UNORM case ETextureCompression.TCM_None: return(EFormat.R8G8B8A8_UNORM); //0x07 // exception: characters\models\animals\goose\model\t_01__goose_d01.xbm has 0x07 but TCM_DXTAlpha case ETextureCompression.TCM_DXTNoAlpha: case ETextureCompression.TCM_Normals: return(EFormat.BC1_UNORM); //0x08 // exception: characters\models\animals\goose\model\t_01__goose_d01.xbm has 0x07 but TCM_DXTAlpha case ETextureCompression.TCM_DXTAlpha: case ETextureCompression.TCM_NormalsHigh: case ETextureCompression.TCM_NormalsGloss: return(EFormat.BC3_UNORM); case ETextureCompression.TCM_QualityColor: return(EFormat.BC7_UNORM); //0x0A // missing: 0x0D //EFormat.BC2_UNORM // used for not imported dds files in texturecache therefore will never come up here case ETextureCompression.TCM_QualityR: return(EFormat.BC4_UNORM); //0x0E case ETextureCompression.TCM_QualityRG: return(EFormat.BC5_UNORM); //0x0F case ETextureCompression.TCM_DXTAlphaLinear: // unused case ETextureCompression.TCM_RGBE: // unused default: throw new NotImplementedException(); } }
/// <summary> /// Generate DDSMetadata from a Redengine CBitmapTexture /// </summary> /// <param name="xbm"></param> /// <returns></returns> private static DDSMetadata GetDDSMetadata(CBitmapTexture xbm) { int residentMipIndex = xbm.ResidentMipIndex?.val ?? 0; int mipcount = xbm.Mipdata.elements.Count - residentMipIndex; uint width = xbm.Mipdata.elements[residentMipIndex].Width.val; uint height = xbm.Mipdata.elements[residentMipIndex].Height.val; ETextureCompression compression = xbm.Compression.WrappedEnum; var ddsformat = ImageUtility.GetEFormatFromCompression(compression); // TODO: TEST THIS if (ddsformat == EFormat.R8G8B8A8_UNORM) { ETextureRawFormat format = xbm.Format.WrappedEnum; switch (format) { case ETextureRawFormat.TRF_Grayscale: // only this is ever used break; case ETextureRawFormat.TRF_TrueColor: // this is set if format is NULL case ETextureRawFormat.TRF_HDR: case ETextureRawFormat.TRF_AlphaGrayscale: case ETextureRawFormat.TRF_HDRGrayscale: default: ddsformat = EFormat.R8G8B8A8_UNORM; //throw new Exception("Invalid texture format type! [" + format + "]"); break; } } return(new DDSMetadata(width, height, (uint)mipcount, ddsformat)); }
/// <summary> /// Gets the DXGI format for CP77 dds buffers from a given ETextureCompression and ETextureRawFormat /// </summary> /// <param name="compression"></param> /// <param name="rawFormat"></param> /// <returns></returns> /// <exception cref="NotImplementedException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static EFormat GetDXGIFormat(ETextureCompression compression, ETextureRawFormat rawFormat) { switch (compression) { case ETextureCompression.TCM_QualityR: return(EFormat.BC4_UNORM); case ETextureCompression.TCM_QualityRG: case ETextureCompression.TCM_Normalmap: return(EFormat.BC5_UNORM); case ETextureCompression.TCM_QualityColor: return(EFormat.BC7_UNORM); case ETextureCompression.TCM_DXTNoAlpha: case ETextureCompression.TCM_Normals_DEPRECATED: return(EFormat.BC1_UNORM); case ETextureCompression.TCM_DXTAlphaLinear: case ETextureCompression.TCM_DXTAlpha: return(EFormat.BC3_UNORM); case ETextureCompression.TCM_None: { switch (rawFormat) { case ETextureRawFormat.TRF_Invalid: case ETextureRawFormat.TRF_TrueColor: return(EFormat.R8G8B8A8_UNORM); case ETextureRawFormat.TRF_DeepColor: return(EFormat.R10G10B10A2_UNORM); case ETextureRawFormat.TRF_HDRFloat: return(EFormat.R32G32B32A32_FLOAT); case ETextureRawFormat.TRF_HDRHalf: return(EFormat.R16G16B16A16_FLOAT); case ETextureRawFormat.TRF_HDRFloatGrayscale: return(EFormat.R16_FLOAT); case ETextureRawFormat.TRF_R8G8: return(EFormat.R8G8_UNORM); case ETextureRawFormat.TRF_Grayscale: return(EFormat.R8_UINT); case ETextureRawFormat.TRF_AlphaGrayscale: return(EFormat.A8_UNORM); case ETextureRawFormat.TRF_Grayscale_Font: throw new NotImplementedException(); case ETextureRawFormat.TRF_R32UI: //return EFormat.R32_UINT; throw new NotImplementedException(); default: throw new ArgumentOutOfRangeException(nameof(rawFormat), rawFormat, null); } } case ETextureCompression.TCM_RGBE: case ETextureCompression.TCM_Normals: case ETextureCompression.TCM_NormalsHigh_DEPRECATED: case ETextureCompression.TCM_NormalsHigh: case ETextureCompression.TCM_NormalsGloss_DEPRECATED: case ETextureCompression.TCM_NormalsGloss: case ETextureCompression.TCM_TileMap: case ETextureCompression.TCM_HalfHDR_Unsigned: case ETextureCompression.TCM_HalfHDR: case ETextureCompression.TCM_HalfHDR_Signed: case ETextureCompression.TCM_Max: throw new NotImplementedException(); default: throw new ArgumentOutOfRangeException(nameof(compression), compression, null); } }
private static DDSMetadata Xbm2Ddsheader(CBitmapTexture xbm) { try { int residentMipIndex = xbm.GetVariableByName("ResidentMipIndex") == null ? 0 : (int)((CUInt8)xbm.GetVariableByName("ResidentMipIndex")).val; int mipcount; // handle cooked xbms if (xbm.GetVariableByName("SourceData") == null) { mipcount = xbm.Mipdata.elements.Count - residentMipIndex; } // handle imported xbms else { mipcount = 0; } uint width = xbm.Mipdata.elements[residentMipIndex].Width.val; uint height = xbm.Mipdata.elements[residentMipIndex].Height.val; var ecompression = (CName)xbm.GetVariableByName("compression"); ETextureCompression compression = (ETextureCompression)Enum.Parse(typeof(ETextureCompression), ecompression.Value); var eformat = (CName)xbm.GetVariableByName("format"); ETextureRawFormat format = ETextureRawFormat.TRF_TrueColor; if (eformat != null) { format = (ETextureRawFormat)Enum.Parse(typeof(ETextureRawFormat), eformat.Value); } var ddsformat = ETextureFormat.TEXFMT_R8G8B8A8; switch (compression) { case ETextureCompression.TCM_DXTNoAlpha: ddsformat = ETextureFormat.TEXFMT_BC1; break; case ETextureCompression.TCM_DXTAlpha: ddsformat = ETextureFormat.TEXFMT_BC3; break; case ETextureCompression.TCM_Normals: ddsformat = ETextureFormat.TEXFMT_BC1; break; case ETextureCompression.TCM_NormalsHigh: ddsformat = ETextureFormat.TEXFMT_BC3; break; case ETextureCompression.TCM_NormalsGloss: ddsformat = ETextureFormat.TEXFMT_BC3; break; case ETextureCompression.TCM_QualityR: ddsformat = ETextureFormat.TEXFMT_BC4; break; case ETextureCompression.TCM_QualityRG: ddsformat = ETextureFormat.TEXFMT_BC5; break; case ETextureCompression.TCM_QualityColor: ddsformat = ETextureFormat.TEXFMT_BC3; break; case ETextureCompression.TCM_DXTAlphaLinear: case ETextureCompression.TCM_RGBE: case ETextureCompression.TCM_None: switch (format) { case ETextureRawFormat.TRF_TrueColor: ddsformat = ETextureFormat.TEXFMT_R8G8B8A8; break; case ETextureRawFormat.TRF_Grayscale: break; case ETextureRawFormat.TRF_HDR: case ETextureRawFormat.TRF_AlphaGrayscale: case ETextureRawFormat.TRF_HDRGrayscale: default: throw new Exception("Invalid compression type! [" + compression + "]"); } break; default: throw new Exception("Invalid compression type! [" + compression + "]"); } return(new DDSMetadata(width, height, (uint)mipcount, ddsformat)); } catch (Exception e) { //string message = e.Message; //string caption = "Error!"; //MessageBoxButtons buttons = MessageBoxButtons.OK; //MessageBox.Show(message, caption, buttons); throw e; } }
private static async Task <int> DumpDDSInfo(DumpDDSOptions options) { var dt = DateTime.Now; string idx = RED.CRC32.Crc32Algorithm.Compute(Encoding.ASCII.GetBytes($"{dt.Year}{dt.Month}{dt.Day}{dt.Hour}{dt.Minute}{dt.Second}")).ToString(); var outDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "DDSTest", $"ExtractedFiles_{idx}"); using (var pb = new ProgressBar()) { if (!Directory.Exists(outDir)) { Directory.CreateDirectory(outDir); } var txc = new TextureManager(); //using (var p11 = pb.Progress.Fork(0.25, "Loading TextureManager")) { txc.LoadAll("C:\\Steam\\steamapps\\common\\The Witcher 3\\bin\\x64"); } System.Console.WriteLine($"Loaded TextureManager"); //combined bundle dump // load xbm bundle infos var bundlexbmDict = new Dictionary <uint, XBMBundleInfo>(); var mc = new BundleManager(); //using (var p12 = pb.Progress.Fork(0.25, "Loading BundleManager")) { mc.LoadAll("C:\\Steam\\steamapps\\common\\The Witcher 3\\bin\\x64"); } System.Console.WriteLine($"Loaded BundleManager"); //using (var p2 = pb.Progress.Fork(0.5, "Loading Bundle Info")) using (var p2 = pb.Progress.Fork(0.5, "Bundle Info")) { var filesb = mc.FileList.Where(x => x.Name.EndsWith("xbm")).ToList(); for (int i = 0; i < filesb.Count; i++) { var f = filesb[i]; try { var buff = new byte[f.Size]; using (var s = new MemoryStream()) { f.Extract(s); using (var ms = new MemoryStream(s.ToArray())) using (var br = new BinaryReader(ms)) { var crw = new CR2WFile(); crw.Read(br); foreach (var c in crw.chunks) { if (c.data is CBitmapTexture) { var x = c.data as CBitmapTexture; if (!bundlexbmDict.ContainsKey(((CUInt32)x.GetVariableByName("textureCacheKey")).val)) { var ecompression = (CName)x.GetVariableByName("compression"); ETextureCompression compression = (ETextureCompression)Enum.Parse(typeof(ETextureCompression), ecompression.Value); var eformat = (CName)x.GetVariableByName("format"); ETextureRawFormat format = (ETextureRawFormat)Enum.Parse(typeof(ETextureRawFormat), eformat.Value); bundlexbmDict.Add(((CUInt32)x.GetVariableByName("textureCacheKey")).val, new XBMBundleInfo() { Name = f.Name, Width = (CUInt32)x.GetVariableByName("width") == null ? 0 : ((CUInt32)x.GetVariableByName("width")).val, Height = (CUInt32)x.GetVariableByName("width") == null ? 0 : ((CUInt32)x.GetVariableByName("height")).val, Format = format, Compression = compression, TextureGroup = (CName)x.GetVariableByName("textureGroup") == null ? "" : ((CName)x.GetVariableByName("textureGroup")).Value, } ); } else { } } } } } } catch (Exception ex) { throw ex; } p2.Report(i / (double)filesb.Count, $"Loading bundle entries: {i}/{filesb.Count}"); } } System.Console.WriteLine($"Loaded {bundlexbmDict.Values.Count} Bundle Entries"); using (var p3 = pb.Progress.Fork(0.5, "Cache Info")) { // Dump texture cache infos using (StreamWriter writer = File.CreateText(Path.Combine(outDir, $"__ddsdump_{idx}.txt"))) { string head = "Format1\t" + "Format2\t" + "BPP\t" + "Width\t" + "Height\t" + "Size\t" + "Mips\t" + "Slices\t" + "Cube\t" + "Unk1\t" + "Hash\t" + "Name\t"; head += "XBMFormat\t" + "XBMCompression\t" + "XBMTExtureGroup\t" ; writer.WriteLine(head); //string ext = "xbm"; //var files = txc.FileList.Where(x => x.Name.EndsWith(ext)).ToList(); var files = txc.FileList; for (int j = 0; j < files.Count; j++) { IWitcherFile f = files[j]; TextureCacheItem x = f as TextureCacheItem; string info = $"{x.Type1}/{x.Type1:X2}\t" + $"{x.Type2}/{x.Type2:X2}\t" + $"{x.BaseAlignment}\t" + $"{x.BaseWidth}\t" + $"{x.BaseHeight}\t" + $"{x.Size}\t" + $"{x.Mipcount}\t" + $"{x.SliceCount}\t" + $"{x.IsCube:X2}\t" + $"{x.Unk1}/{x.Unk1:X2}\t" + $"{x.Hash}\t" + $"{x.Name}\t" ; //info += "<"; //foreach (var y in x.MipMapInfo) //{ // info += $"<{y.Item1},{y.Item2}>"; //} //info += ">"; if (bundlexbmDict.ContainsKey(x.Hash)) { XBMBundleInfo bundleinfo = bundlexbmDict[x.Hash]; info += //$"{bundleinfo.Width}\t" + //$"{bundleinfo.Height}\t" + $"{bundleinfo.Format}\t" + $"{bundleinfo.Compression}\t" + $"{bundleinfo.TextureGroup}\t" ; } else { } //System.Console.WriteLine(info); writer.WriteLine(info); p3.Report(j / (double)files.Count, $"Dumping cache entries: {j}/{files.Count}"); } System.Console.WriteLine($"Finished dumping {files.Count} texture cache infos.\r\n"); } } } System.Console.WriteLine($"Finished.\r\n"); System.Console.ReadLine(); return(1); }