public byte[] GetImageBytesForMip(Texture2DMipInfo info, GameTarget target, bool useLowerMipsIfTFCMissing, List <string> additionalTFCs = null) { byte[] imageBytes = null; try { imageBytes = GetTextureData(info, target, additionalTFCs: additionalTFCs); } catch (FileNotFoundException e) { if (useLowerMipsIfTFCMissing) { Debug.WriteLine("External cache not found. Defaulting to internal mips."); //External archive not found - using built in mips (will be hideous, but better than nothing) info = Mips.FirstOrDefault(x => x.storageType == StorageTypes.pccUnc); if (info != null) { imageBytes = GetTextureData(info, target); } } else { throw e; //rethrow } } if (imageBytes == null) { throw new Exception("Could not fetch texture data for texture " + info?.Export.ObjectName); } return(imageBytes); }
public static List <Texture2DMipInfo> GetTexture2DMipInfos(ExportEntry exportEntry, string cacheName) { MemoryStream ms = new MemoryStream(exportEntry.Data); ms.Seek(exportEntry.propsEnd(), SeekOrigin.Begin); if (exportEntry.FileRef.Game != Mod.MEGame.ME3) { ms.Skip(4); //BulkDataFlags ms.Skip(4); //ElementCount int bulkDataSize = ms.ReadInt32(); ms.Seek(4, SeekOrigin.Current); // position in the package ms.Skip(bulkDataSize); //skips over thumbnail png, if it exists } var mips = new List <Texture2DMipInfo>(); int numMipMaps = ms.ReadInt32(); for (int l = 0; l < numMipMaps; l++) { Texture2DMipInfo mip = new Texture2DMipInfo { Export = exportEntry, index = l, storageType = (StorageTypes)ms.ReadInt32(), uncompressedSize = ms.ReadInt32(), compressedSize = ms.ReadInt32(), externalOffset = ms.ReadInt32(), localExportOffset = (int)ms.Position, TextureCacheName = cacheName //If this is ME1, this will simply be ignored in the setter }; switch (mip.storageType) { case StorageTypes.pccUnc: ms.Seek(mip.uncompressedSize, SeekOrigin.Current); break; case StorageTypes.pccLZO: case StorageTypes.pccZlib: ms.Seek(mip.compressedSize, SeekOrigin.Current); break; } mip.width = ms.ReadInt32(); mip.height = ms.ReadInt32(); if (mip.width == 4 && mips.Exists(m => m.width == mip.width)) { mip.width = mips.Last().width / 2; } if (mip.height == 4 && mips.Exists(m => m.height == mip.height)) { mip.height = mips.Last().height / 2; } if (mip.width == 0) { mip.width = 1; } if (mip.height == 0) { mip.height = 1; } mips.Add(mip); } return(mips); }
public static byte[] GetTextureData(Texture2DMipInfo mipToLoad, GameTarget target, bool decompress = true, List <string> additionalTFCs = null) { var imagebytes = new byte[decompress ? mipToLoad.uncompressedSize : mipToLoad.compressedSize]; //Debug.WriteLine("getting texture data for " + mipToLoad.Export.FullPath); if (mipToLoad.storageType == StorageTypes.pccUnc) { Buffer.BlockCopy(mipToLoad.Export.Data, mipToLoad.localExportOffset, imagebytes, 0, mipToLoad.uncompressedSize); } else if (mipToLoad.storageType == StorageTypes.pccLZO || mipToLoad.storageType == StorageTypes.pccZlib) { if (decompress) { try { TextureCompression.DecompressTexture(imagebytes, new MemoryStream(mipToLoad.Export.Data, mipToLoad.localExportOffset, mipToLoad.compressedSize), mipToLoad.storageType, mipToLoad.uncompressedSize, mipToLoad.compressedSize); } catch (Exception e) { throw new Exception($"{e.Message}\nStorageType: {mipToLoad.storageType}\n"); } } else { Buffer.BlockCopy(mipToLoad.Export.Data, mipToLoad.localExportOffset, imagebytes, 0, mipToLoad.compressedSize); } } else if (mipToLoad.storageType == StorageTypes.extUnc || mipToLoad.storageType == StorageTypes.extLZO || mipToLoad.storageType == StorageTypes.extZlib) { string filename = null; List <string> loadedFiles = MEDirectories.EnumerateGameFiles(target.Game, target.TargetPath); if (mipToLoad.Export.Game == Mod.MEGame.ME1) { var fullPath = loadedFiles.FirstOrDefault(x => Path.GetFileName(x).Equals(mipToLoad.TextureCacheName, StringComparison.InvariantCultureIgnoreCase)); if (fullPath != null) { filename = fullPath; } else { throw new FileNotFoundException($"Externally referenced texture file not found in game: {mipToLoad.TextureCacheName}."); } } else { string archive = mipToLoad.TextureCacheName + ".tfc"; var localDirectoryTFCPath = Path.Combine(Path.GetDirectoryName(mipToLoad.Export.FileRef.FilePath), archive); if (File.Exists(localDirectoryTFCPath)) { filename = localDirectoryTFCPath; } else if (additionalTFCs != null && additionalTFCs.Any(x => Path.GetFileName(x).Equals(archive, StringComparison.InvariantCultureIgnoreCase))) { filename = additionalTFCs.First(x => Path.GetFileName(x).Equals(archive, StringComparison.InvariantCultureIgnoreCase)); } else { var tfcs = loadedFiles.Where(x => x.EndsWith(@".tfc")).ToList(); var fullPath = loadedFiles.FirstOrDefault(x => Path.GetFileName(x).Equals(archive, StringComparison.InvariantCultureIgnoreCase)); if (fullPath != null) { filename = fullPath; } else { throw new FileNotFoundException($@"Externally referenced texture cache not found: {archive}."); } } } //exceptions above will prevent filename from being null here try { using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) { try { fs.Seek(mipToLoad.externalOffset, SeekOrigin.Begin); if (mipToLoad.storageType == StorageTypes.extLZO || mipToLoad.storageType == StorageTypes.extZlib) { if (decompress) { using (MemoryStream tmpStream = new MemoryStream(fs.ReadToBuffer(mipToLoad.compressedSize))) { try { TextureCompression.DecompressTexture(imagebytes, tmpStream, mipToLoad.storageType, mipToLoad.uncompressedSize, mipToLoad.compressedSize); } catch (Exception e) { throw new Exception(e.Message + "\n" + "File: " + filename + "\n" + "StorageType: " + mipToLoad.storageType + "\n" + "External file offset: " + mipToLoad.externalOffset); } } } else { fs.Read(imagebytes, 0, mipToLoad.compressedSize); } } else { fs.Read(imagebytes, 0, mipToLoad.uncompressedSize); } } catch (Exception e) { throw new Exception(e.Message + "\n" + "File: " + filename + "\n" + "StorageType: " + mipToLoad.storageType + "\n" + "External file offset: " + mipToLoad.externalOffset); } } } catch (Exception e) { throw new Exception(e.Message + "\n" + "File: " + filename + "\n" + "StorageType: " + mipToLoad.storageType + "\n" + "External file offset: " + mipToLoad.externalOffset); } } return(imagebytes); }