public void Save(Stream output, DdsSaveConfig saveConfig) { BinaryWriter writer = new BinaryWriter(output); header = new DdsHeader(saveConfig, Width, Height); header.Write(writer); if (saveConfig.GenerateMipMaps) { GenerateMipMaps(); } foreach (DdsMipMap mipMap in MipMaps.OrderByDescending(mip => mip.Width)) { byte[] outputData = WriteMipMap(mipMap, saveConfig); output.Write(outputData, 0, outputData.Length); } output.Flush(); }
private async Task exportFileObjects(List<DomainUpkFile> files) { LoadProgressMessage message = new LoadProgressMessage { Text = "Exporting...", Total = files.Count }; int compressor = menuViewModel.IsCompressorClusterFit ? 0 : menuViewModel.IsCompressorRangeFit ? 1 : 2; int errorMetric = menuViewModel.IsErrorMetricPerceptual ? 0 : 1; DdsSaveConfig config = new DdsSaveConfig(FileFormat.Unknown, compressor, errorMetric, menuViewModel.IsWeightColorByAlpha, false); foreach(DomainUpkFile file in files) { FileViewEntity fileEntity = viewModel.Files.Single(fe => fe.Id == file.Id); string directory = Path.Combine(settings.ExportPath, Path.GetDirectoryName(file.GameFilename), Path.GetFileNameWithoutExtension(file.GameFilename)); if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); DomainHeader header = file.Header; if (header == null) { try { header = await repository.LoadUpkFile(Path.Combine(settings.PathToGame, file.GameFilename)); await Task.Run(() => header.ReadHeaderAsync(null)); } catch(Exception ex) { messenger.Send(new ApplicationErrorMessage { HeaderText = "Error Loading UPK File", ErrorMessage = $"Filename: {file.GameFilename}", Exception = ex }); fileEntity.IsErrored = true; continue; } } message.Current++; foreach(DomainExportTableEntry export in header.ExportTable) { if (export.DomainObject == null) { try { await export.ParseDomainObject(header, false, false); } catch(Exception ex) { messenger.Send(new ApplicationErrorMessage { HeaderText = "Error Parsing Object", ErrorMessage = $"Filename: {header.Filename}\nExport Name: {export.NameTableIndex.Name}\nType: {export.TypeReferenceNameIndex.Name}", Exception = ex }); fileEntity.IsErrored = true; continue; } } if (!export.DomainObject.IsExportable) continue; string filename = Path.Combine(directory, $"{export.NameTableIndex.Name}{export.DomainObject.FileExtension}"); message.StatusText = filename; messenger.Send(message); await export.DomainObject.SaveObject(filename, config); } file.Header = null; } message.IsComplete = true; message.StatusText = null; messenger.Send(message); }
public DdsHeader(DdsSaveConfig config, int width, int height) { PixelFormat = new DdsPixelFormat(config.FileFormat); bool isCompressed = config.FileFormat == FileFormat.DXT1 || config.FileFormat == FileFormat.DXT3 || config.FileFormat == FileFormat.DXT5; // // Compute mip map count.. // int mipCount = config.GenerateMipMaps ? CountMipMaps(width, height) : 1; Size = 18 * 4 + PixelFormat.Size + 5 * 4; Width = (uint)width; Height = (uint)height; MipMapCount = mipCount == 1 ? 0 : (uint)mipCount; HeaderFlags = (uint)(Constants.HeaderFlags.Texture | (isCompressed ? Constants.HeaderFlags.LinearSize : Constants.HeaderFlags.Pitch) | (mipCount > 1 ? Constants.HeaderFlags.MipMap : Constants.HeaderFlags.None)); SurfaceFlags = (uint)(Constants.SurfaceFlags.Texture | (mipCount > 1 ? Constants.SurfaceFlags.MipMap : Constants.SurfaceFlags.None)); if (isCompressed) { // // Compresssed textures have the linear flag set. So pitchOrLinearSize // needs to contain the entire size of the DXT block. // int blockCount = (width + 3) / 4 * ((height + 3) / 4); int blockSize = config.FileFormat == FileFormat.Unknown ? 8 : 16; PitchOrLinearSize = (uint)(blockCount * blockSize); } else { // // Non-compressed textures have the pitch flag set. So pitchOrLinearSize // needs to contain the row pitch of the main image. // int pixelWidth = 0; switch (config.FileFormat) { case FileFormat.A8R8G8B8: case FileFormat.X8R8G8B8: case FileFormat.A8B8G8R8: case FileFormat.X8B8G8R8: { pixelWidth = 4; break; } case FileFormat.R8G8B8: { pixelWidth = 3; break; } case FileFormat.A1R5G5B5: case FileFormat.A4R4G4B4: case FileFormat.R5G6B5: { pixelWidth = 2; break; } case FileFormat.G8: { pixelWidth = 1; break; } } PitchOrLinearSize = (uint)(width * pixelWidth); } }
public byte[] WriteMipMap(DdsMipMap mipMap, DdsSaveConfig saveConfig) { byte[] outputData; if (saveConfig.FileFormat >= FileFormat.DXT1 && saveConfig.FileFormat <= FileFormat.DXT5) { outputData = DdsSquish.CompressImage(mipMap.MipMap, mipMap.Width, mipMap.Height, saveConfig.GetSquishFlags(), null); } else { int pixelWidth = (int)header.PitchOrLinearSize / Width; int mipPitch = pixelWidth * mipMap.Width; outputData = new byte[mipPitch * mipMap.Height]; outputData.Initialize(); for (int i = 0; i < mipMap.MipMap.Length; i += 4) { uint pixelData = 0; byte R = mipMap.MipMap[i + 0]; byte G = mipMap.MipMap[i + 1]; byte B = mipMap.MipMap[i + 2]; byte A = mipMap.MipMap[i + 3]; switch (saveConfig.FileFormat) { case FileFormat.A8R8G8B8: { pixelData = ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | ((uint)B << 0); break; } case FileFormat.X8R8G8B8: { pixelData = ((uint)R << 16) | ((uint)G << 8) | ((uint)B << 0); break; } case FileFormat.A8B8G8R8: { pixelData = ((uint)A << 24) | ((uint)B << 16) | ((uint)G << 8) | ((uint)R << 0); break; } case FileFormat.X8B8G8R8: { pixelData = ((uint)B << 16) | ((uint)G << 8) | ((uint)R << 0); break; } case FileFormat.A1R5G5B5: { pixelData = ((uint)(A != 0 ? 1 : 0) << 15) | ((uint)(R >> 3) << 10) | ((uint)(G >> 3) << 5) | ((uint)(B >> 3) << 0); break; } case FileFormat.A4R4G4B4: { pixelData = ((uint)(A >> 4) << 12) | ((uint)(R >> 4) << 8) | ((uint)(G >> 4) << 4) | ((uint)(B >> 4) << 0); break; } case FileFormat.R8G8B8: { pixelData = ((uint)R << 16) | ((uint)G << 8) | ((uint)B << 0); break; } case FileFormat.R5G6B5: { pixelData = ((uint)(R >> 3) << 11) | ((uint)(G >> 2) << 5) | ((uint)(B >> 3) << 0); break; } case FileFormat.G8: { pixelData = (uint)((R + G + B) / 3.0 + 0.5); break; } } int pixelOffset = i / 4 * pixelWidth; for (int j = 0; j < pixelWidth; j++) { outputData[pixelOffset + j] = (byte)((pixelData >> (8 * j)) & 0xff); } } } return(outputData); }
public byte[] WriteMipMap(DdsMipMap mipMap , DdsSaveConfig saveConfig) { byte[] outputData; if (saveConfig.FileFormat >= FileFormat.DXT1 && saveConfig.FileFormat <= FileFormat.DXT5) { outputData = DdsSquish.CompressImage(mipMap.MipMap, mipMap.Width, mipMap.Height, saveConfig.GetSquishFlags(), null); } else { int pixelWidth = (int)header.PitchOrLinearSize / Width; int mipPitch = pixelWidth * mipMap.Width; outputData = new byte[mipPitch * mipMap.Height]; outputData.Initialize(); for(int i = 0; i < mipMap.MipMap.Length; i += 4) { uint pixelData = 0; byte R = mipMap.MipMap[i + 0]; byte G = mipMap.MipMap[i + 1]; byte B = mipMap.MipMap[i + 2]; byte A = mipMap.MipMap[i + 3]; switch(saveConfig.FileFormat) { case FileFormat.A8R8G8B8: { pixelData = ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | ((uint)B << 0); break; } case FileFormat.X8R8G8B8: { pixelData = ((uint)R << 16) | ((uint)G << 8) | ((uint)B << 0); break; } case FileFormat.A8B8G8R8: { pixelData = ((uint)A << 24) | ((uint)B << 16) | ((uint)G << 8) | ((uint)R << 0); break; } case FileFormat.X8B8G8R8: { pixelData = ((uint)B << 16) | ((uint)G << 8) | ((uint)R << 0); break; } case FileFormat.A1R5G5B5: { pixelData = ((uint)(A != 0 ? 1 : 0) << 15) | ((uint)(R >> 3) << 10) | ((uint)(G >> 3) << 5) | ((uint)(B >> 3) << 0); break; } case FileFormat.A4R4G4B4: { pixelData = ((uint)(A >> 4) << 12) | ((uint)(R >> 4) << 8) | ((uint)(G >> 4) << 4) | ((uint)(B >> 4) << 0); break; } case FileFormat.R8G8B8: { pixelData = ((uint)R << 16) | ((uint)G << 8) | ((uint)B << 0); break; } case FileFormat.R5G6B5: { pixelData = ((uint)(R >> 3) << 11) | ((uint)(G >> 2) << 5) | ((uint)(B >> 3) << 0); break; } case FileFormat.G8: { pixelData = (uint)((R + G + B) / 3.0 + 0.5); break; } } int pixelOffset = i / 4 * pixelWidth; for(int j = 0; j < pixelWidth; j++) outputData[pixelOffset + j] = (byte)((pixelData >> (8 * j)) & 0xff); } } return outputData; }
public void Save(Stream output, DdsSaveConfig saveConfig) { BinaryWriter writer = new BinaryWriter(output); header = new DdsHeader(saveConfig, Width, Height); header.Write(writer); if (saveConfig.GenerateMipMaps) GenerateMipMaps(); foreach(DdsMipMap mipMap in MipMaps.OrderByDescending(mip => mip.Width)) { byte[] outputData = WriteMipMap(mipMap, saveConfig); output.Write(outputData, 0, outputData.Length); } output.Flush(); }
private async Task rebuildExports() { Dictionary<ExportedObjectViewEntity, List<ExportedObjectViewEntity>> filesToMod = viewModel.ExportsTree?.Traverse(e => Path.HasExtension(e.Filename) && e.IsChecked) .GroupBy(e => e.Parent) .ToDictionary(g => g.Key, g => g.ToList()); if (filesToMod == null || !filesToMod.Any()) return; LoadProgressMessage message = new LoadProgressMessage { Text = "Rebuilding...", Total = filesToMod.Count }; foreach(KeyValuePair<ExportedObjectViewEntity, List<ExportedObjectViewEntity>> pair in filesToMod) { string gameFilename = $"{pair.Key.Filename.Replace(settings.ExportPath, null)}.upk"; DomainUpkFile file = allFiles.SingleOrDefault(f => f.GameFilename.Equals(gameFilename)); if (file == null) continue; DomainHeader header = await repository.LoadUpkFile(Path.Combine(settings.PathToGame, file.GameFilename)); await header.ReadHeaderAsync(null); message.Current++; foreach(ExportedObjectViewEntity entity in pair.Value) { DomainExportTableEntry export = header.ExportTable.SingleOrDefault(ex => ex.NameTableIndex.Name.Equals(Path.GetFileNameWithoutExtension(entity.Filename), StringComparison.CurrentCultureIgnoreCase)); if (export == null) continue; await export.ParseDomainObject(header, false, false); int compressor = menuViewModel.IsCompressorClusterFit ? 0 : menuViewModel.IsCompressorRangeFit ? 1 : 2; int errorMetric = menuViewModel.IsErrorMetricPerceptual ? 0 : 1; DdsSaveConfig config = new DdsSaveConfig(FileFormat.Unknown, compressor, errorMetric, menuViewModel.IsWeightColorByAlpha, false); await export.DomainObject.SetObject(entity.Filename, header.NameTable, config); message.StatusText = entity.Filename; messenger.Send(message); } string directory = Path.Combine(settings.PathToGame, Path.GetDirectoryName(file.GameFilename), "mod"); string filename = Path.Combine(directory, Path.GetFileName(file.GameFilename)); if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); await repository.SaveUpkFile(header, filename); DomainUpkFile upkFile = new DomainUpkFile { GameFilename = filename.Replace(settings.PathToGame, null), FileSize = new FileInfo(filename).Length }; messenger.Send(new ModFileBuiltMessage { UpkFile = upkFile }); } message.IsComplete = true; message.StatusText = null; messenger.Send(message); }
public DdsHeader(DdsSaveConfig config, int width, int height) { PixelFormat = new DdsPixelFormat(config.FileFormat); bool isCompressed = config.FileFormat == FileFormat.DXT1 || config.FileFormat == FileFormat.DXT3 || config.FileFormat == FileFormat.DXT5; // // Compute mip map count.. // int mipCount = config.GenerateMipMaps ? CountMipMaps(width, height) : 1; Size = 18 * 4 + PixelFormat.Size + 5 * 4; Width = (uint)width; Height = (uint)height; MipMapCount = mipCount == 1 ? 0 : (uint)mipCount; HeaderFlags = (uint)(Constants.HeaderFlags.Texture | (isCompressed ? Constants.HeaderFlags.LinearSize : Constants.HeaderFlags.Pitch) | (mipCount > 1 ? Constants.HeaderFlags.MipMap : Constants.HeaderFlags.None)); SurfaceFlags = (uint)(Constants.SurfaceFlags.Texture | (mipCount > 1 ? Constants.SurfaceFlags.MipMap : Constants.SurfaceFlags.None)); if (isCompressed) { // // Compresssed textures have the linear flag set. So pitchOrLinearSize // needs to contain the entire size of the DXT block. // int blockCount = (width + 3) / 4 * ((height + 3) / 4); int blockSize = config.FileFormat == FileFormat.Unknown ? 8 : 16; PitchOrLinearSize = (uint)(blockCount * blockSize); } else { // // Non-compressed textures have the pitch flag set. So pitchOrLinearSize // needs to contain the row pitch of the main image. // int pixelWidth = 0; switch(config.FileFormat) { case FileFormat.A8R8G8B8: case FileFormat.X8R8G8B8: case FileFormat.A8B8G8R8: case FileFormat.X8B8G8R8: { pixelWidth = 4; break; } case FileFormat.R8G8B8: { pixelWidth = 3; break; } case FileFormat.A1R5G5B5: case FileFormat.A4R4G4B4: case FileFormat.R5G6B5: { pixelWidth = 2; break; } case FileFormat.G8: { pixelWidth = 1; break; } } PitchOrLinearSize = (uint)(width * pixelWidth); } }
private async Task onSaveObjectAsExecute() { VistaSaveFileDialog sfd = new VistaSaveFileDialog { FileName = export.NameTableIndex.Name, DefaultExt = export.DomainObject.FileExtension, Filter = $"{export.DomainObject.FileTypeDesc}|*{export.DomainObject.FileExtension}", Title = "Save Object As..." }; bool? result = sfd.ShowDialog(); if (!result.HasValue || !result.Value) return; int compressor = menuViewModel.IsCompressorClusterFit ? 0 : menuViewModel.IsCompressorRangeFit ? 1 : 2; int errorMetric = menuViewModel.IsErrorMetricPerceptual ? 0 : 1; DdsSaveConfig config = new DdsSaveConfig(FileFormat.Unknown, compressor, errorMetric, menuViewModel.IsWeightColorByAlpha, false); await export.DomainObject.SaveObject(sfd.FileName, config); }