Skip to content

Commit

Permalink
dds merge
Browse files Browse the repository at this point in the history
  • Loading branch information
diogotr7 committed Dec 30, 2024
1 parent 687bb78 commit a3ed33c
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 90 deletions.
13 changes: 11 additions & 2 deletions src/StarBreaker.CryXmlB/CryXml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using StarBreaker.Common;

namespace StarBreaker.CryXmlB;
Expand Down Expand Up @@ -50,7 +51,7 @@ public static bool IsCryXmlB(ReadOnlySpan<byte> data)
return data.Length > magicLength && data[..magicLength].SequenceEqual(magic);
}

public void WriteXml(XmlWriter writer)
public void WriteTo(XmlWriter writer)
{
if (_nodes[0].ParentIndex != -1)
throw new Exception("Root node has parent");
Expand Down Expand Up @@ -81,6 +82,14 @@ private void WriteXmlElement(XmlWriter writer, int nodeIndex)
writer.WriteEndElement();
}

public XDocument ToXml()
{
var doc = new XDocument();
using var writer = doc.CreateWriter();
WriteTo(writer);
return doc;
}

private static string GetString(Span<byte> data, int offset)
{
var relevantData = data[offset..];
Expand All @@ -96,7 +105,7 @@ public override string ToString()
{
var sb = new StringBuilder();
using var writer = XmlWriter.Create(sb, new XmlWriterSettings { Indent = true });
WriteXml(writer);
WriteTo(writer);
return sb.ToString();
}
}
97 changes: 17 additions & 80 deletions src/StarBreaker.Dds/DdsFile.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
using System.Runtime.InteropServices;
using Pfim;
using SkiaSharp;
using StarBreaker.Common;

namespace StarBreaker.Dds;

public class DdsFile
public static class DdsFile
{
private readonly IImage _image;

public DdsFile(IImage image)
{
_image = image;
}

public static DdsFile FromFile(string fullPath)
public static Stream MergeToStream(string fullPath)
{
if (!fullPath.EndsWith(".dds", StringComparison.OrdinalIgnoreCase))
if (!fullPath.EndsWith(".dds", StringComparison.OrdinalIgnoreCase) && !fullPath.EndsWith(".dds.a", StringComparison.OrdinalIgnoreCase))
throw new ArgumentException("File must be a DDS file");

var containingFolder = Path.GetDirectoryName(fullPath)!;
var files = Directory.GetFiles(containingFolder, Path.GetFileName(fullPath) + ".*").Where(p => char.IsDigit(p[^1]));

var mainFile = new BinaryReader(File.OpenRead(fullPath));

//todo glossmap header

if (mainFile.ReadUInt32() != MemoryMarshal.Read<uint>("DDS "u8))
throw new ArgumentException("File is not a DDS file");

Expand All @@ -37,11 +27,15 @@ public static DdsFile FromFile(string fullPath)
headerLength += 20;

mainFile.BaseStream.Position = 0;

using var ms = new MemoryStream();

var ms = new MemoryStream();

//todo glossmap header

mainFile.BaseStream.CopyAmountTo(ms, (int)headerLength);

//order by the number at the end. e.g. 8 is the largest, 0 is the smallest.
// we want to write the largest mipmap first.
foreach (var ddsFile in files.OrderDescending())
{
using var mipMapStream = new FileStream(ddsFile, FileMode.Open, FileAccess.Read);
Expand All @@ -51,7 +45,14 @@ public static DdsFile FromFile(string fullPath)
mainFile.BaseStream.CopyAmountTo(ms, (int)(mainFile.BaseStream.Length - headerLength));

ms.Position = 0;
return new DdsFile(Pfimage.FromStream(ms));
return ms;
}

public static void MergeToFile(string ddsFullPath, string pngFullPath)
{
using var s = MergeToStream(ddsFullPath);
using var fs = new FileStream(pngFullPath, FileMode.Create, FileAccess.Write, FileShare.None, (int)s.Length, false);
s.CopyTo(fs);
}

private static bool IsGlossMap(ReadOnlySpan<char> path)
Expand All @@ -66,68 +67,4 @@ private static bool IsNormals(ReadOnlySpan<char> path)

return path.EndsWith("ddna.dds") || path.EndsWith("ddna.dds.n") || (char.IsDigit(path[^1]) && path[..^1].EndsWith("ddna.dds"));
}

public SKBitmap ToSkia()
{
SKColorType colorType;

var newData = _image.Data;
var newDataLen = _image.DataLen;
var stride = _image.Stride;
switch (_image.Format)
{
case ImageFormat.Rgb8:
colorType = SKColorType.Gray8;
break;
case ImageFormat.R5g6b5:
// color channels still need to be swapped
colorType = SKColorType.Rgb565;
break;
case ImageFormat.Rgba16:
// color channels still need to be swapped
colorType = SKColorType.Argb4444;
break;
case ImageFormat.Rgb24:
// Skia has no 24bit pixels, so we upscale to 32bit
var pixels = _image.DataLen / 3;
newDataLen = pixels * 4;
newData = new byte[newDataLen];
for (int i = 0; i < pixels; i++)
{
newData[i * 4] = _image.Data[i * 3];
newData[i * 4 + 1] = _image.Data[i * 3 + 1];
newData[i * 4 + 2] = _image.Data[i * 3 + 2];
newData[i * 4 + 3] = 255;
}

stride = _image.Width * 4;
colorType = SKColorType.Bgra8888;
break;
case ImageFormat.Rgba32:
colorType = SKColorType.Bgra8888;
break;
case ImageFormat.R5g5b5:
case ImageFormat.R5g5b5a1:
default:
throw new ArgumentException($"Skia unable to interpret pfim format: {_image.Format}");
}

var imageInfo = new SKImageInfo(_image.Width, _image.Height, colorType);
var handle = GCHandle.Alloc(newData, GCHandleType.Pinned);
var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(newData, 0);
using var data = SKData.Create(ptr, newDataLen, (address, context) => handle.Free());
using var skImage = SKImage.FromPixels(imageInfo, data, stride);
var bitmap = SKBitmap.FromImage(skImage);

return bitmap;
}

public bool SaveAsPng(string fullPath)
{
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
var bitmap = ToSkia();
using var fs = File.Create(Path.ChangeExtension(fullPath, ".png"));
using var wstream = new SKManagedWStream(fs);
return bitmap.Encode(wstream, SKEncodedImageFormat.Png, 80);
}
}
72 changes: 72 additions & 0 deletions src/StarBreaker.Dds/PfimExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Runtime.InteropServices;
using Pfim;
using SkiaSharp;

namespace StarBreaker.Dds;

public static class PfimExtensions
{
public static SKBitmap ToSkia(this IImage _image)
{
SKColorType colorType;

var newData = _image.Data;
var newDataLen = _image.DataLen;
var stride = _image.Stride;
switch (_image.Format)
{
case ImageFormat.Rgb8:
colorType = SKColorType.Gray8;
break;
case ImageFormat.R5g6b5:
// color channels still need to be swapped
colorType = SKColorType.Rgb565;
break;
case ImageFormat.Rgba16:
// color channels still need to be swapped
colorType = SKColorType.Argb4444;
break;
case ImageFormat.Rgb24:
// Skia has no 24bit pixels, so we upscale to 32bit
var pixels = _image.DataLen / 3;
newDataLen = pixels * 4;
newData = new byte[newDataLen];
for (int i = 0; i < pixels; i++)
{
newData[i * 4] = _image.Data[i * 3];
newData[i * 4 + 1] = _image.Data[i * 3 + 1];
newData[i * 4 + 2] = _image.Data[i * 3 + 2];
newData[i * 4 + 3] = 255;
}

stride = _image.Width * 4;
colorType = SKColorType.Bgra8888;
break;
case ImageFormat.Rgba32:
colorType = SKColorType.Bgra8888;
break;
case ImageFormat.R5g5b5:
case ImageFormat.R5g5b5a1:
default:
throw new ArgumentException($"Skia unable to interpret pfim format: {_image.Format}");
}

var imageInfo = new SKImageInfo(_image.Width, _image.Height, colorType);
var handle = GCHandle.Alloc(newData, GCHandleType.Pinned);
var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(newData, 0);
using var data = SKData.Create(ptr, newDataLen, (address, context) => handle.Free());
using var skImage = SKImage.FromPixels(imageInfo, data, stride);
var bitmap = SKBitmap.FromImage(skImage);

return bitmap;
}

public static bool SaveAsPng(this IImage image, string fullPath)
{
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
var bitmap = image.ToSkia();
using var fs = File.Create(Path.ChangeExtension(fullPath, ".png"));
using var wstream = new SKManagedWStream(fs);
return bitmap.Encode(wstream, SKEncodedImageFormat.Png, 80);
}
}
50 changes: 45 additions & 5 deletions src/StarBreaker.Sandbox/DdsUnsplit.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,52 @@
using StarBreaker.Dds;
using Pfim;
using StarBreaker.Dds;

namespace StarBreaker.Sandbox;

public static class DdsUnsplit
{
public static void Run()
public static void Run()
{
Merge();
}

private static void Convert()
{
var things = Directory.GetFiles(@"D:\StarCitizen\P4k", "*.dds", SearchOption.AllDirectories);
Parallel.ForEach(things, file =>
{
Console.WriteLine(file);
try
{
using var mergedDds = DdsFile.MergeToStream(file);
using var image = Pfimage.FromStream(mergedDds);
image.SaveAsPng(file + ".png");
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
}

private static void Merge()
{
var things = Directory.GetFiles(@"D:\StarCitizen\P4k", "*.dds", SearchOption.AllDirectories);
Parallel.ForEach(things, file =>
{
var dds = DdsFile.FromFile(@"C:\Scratch\StarCitizen\p4k\Data\Textures\planets\global\stanton\stanton1\stanton1_clouds_global.dds");
dds.SaveAsPng(@"C:\Scratch\ddd.png");
}
var target = Path.ChangeExtension(file, ".full.dds");
if (File.Exists(target))
return;

Console.WriteLine(file);
try
{
DdsFile.MergeToFile(file, target);
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
}
}
4 changes: 2 additions & 2 deletions src/StarBreaker.Sandbox/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
//TimeP4kExtract.Run();
//TagDatabase.Run();
//StringCrc32c.Run();
// DdsUnsplit.Run();
ZipFile.Run();
DdsUnsplit.Run();
//ZipFile.Run();
2 changes: 1 addition & 1 deletion src/StarBreaker/Screens/Tabs/P4kTabView/P4kTabViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ private void SelectionChanged(object? sender, TreeSelectionModelSelectionChanged
{
_logger.LogInformation("cryxml");
var stringwriter = new StringWriter();
c.WriteXml(XmlWriter.Create(stringwriter, new XmlWriterSettings{Indent = true}));
c.WriteTo(XmlWriter.Create(stringwriter, new XmlWriterSettings{Indent = true}));
preview = new TextPreviewViewModel(stringwriter.ToString());
}
else if (plaintextExtensions.Any(p => selectedEntry.GetName().EndsWith(p, StringComparison.InvariantCultureIgnoreCase)))
Expand Down

0 comments on commit a3ed33c

Please sign in to comment.