diff --git a/Arrowgene.Ddon.Cli/Command/ClientCommand.cs b/Arrowgene.Ddon.Cli/Command/ClientCommand.cs index ddd2aaf5d..9e2c75e19 100644 --- a/Arrowgene.Ddon.Cli/Command/ClientCommand.cs +++ b/Arrowgene.Ddon.Cli/Command/ClientCommand.cs @@ -3,6 +3,7 @@ using System.Text; using System.Text.Json; using Arrowgene.Ddon.Client; +using Arrowgene.Ddon.Client.Resource; using Arrowgene.Logging; namespace Arrowgene.Ddon.Cli.Command @@ -24,6 +25,16 @@ public CommandResultType Run(CommandParameter parameter) return CommandResultType.Exit; } + FileInfo fileInfo = new FileInfo(parameter.Arguments[0]); + if (fileInfo.Exists) + { + if (".tex".Equals(fileInfo.Extension, StringComparison.InvariantCultureIgnoreCase)) + { + TexToDds(fileInfo); + return CommandResultType.Exit; + } + } + DirectoryInfo romDirectory = new DirectoryInfo(parameter.Arguments[0]); if (!romDirectory.Exists) { @@ -44,6 +55,14 @@ public CommandResultType Run(CommandParameter parameter) ExportResourceRepository(romDirectory, outDirectory); return CommandResultType.Exit; } + + if (parameter.ArgumentMap.ContainsKey("extract")) + { + DirectoryInfo outDirectory = new DirectoryInfo(parameter.ArgumentMap["extract"]); + Extract(romDirectory, outDirectory); + return CommandResultType.Exit; + } + return CommandResultType.Exit; } @@ -56,7 +75,7 @@ public void ExportResourceRepository(DirectoryInfo romDirectory, DirectoryInfo o File.WriteAllText(outPath, json); Logger.Info($"Done: {outPath}"); } - + public void DumpPaths(DirectoryInfo romDirectory, DirectoryInfo outDir) { if (outDir == null) @@ -96,6 +115,87 @@ public void DumpPaths(DirectoryInfo romDirectory, DirectoryInfo outDir) Logger.Info($"Done: {outPath}"); } + public void Extract(DirectoryInfo romDirectory, DirectoryInfo outDir) + { + if (outDir == null) + { + Logger.Error("Failed to extract. (outDir == null)"); + return; + } + + if (!outDir.Exists) + { + outDir.Create(); + Logger.Info($"Created Dir: {outDir.FullName}"); + } + + string[] files = Directory.GetFiles(romDirectory.FullName, "*.arc", SearchOption.AllDirectories); + + for (int i = 0; i < files.Length; i++) + { + // TODO utput folder of .arc folder name + string filePath = files[i]; + string relativePath = filePath.Substring(romDirectory.FullName.Length); + ArcArchive archive = new ArcArchive(); + archive.Open(filePath); + foreach (ArcArchive.FileIndex fi in archive.GetFileIndices()) + { + ArcArchive.ArcFile af = archive.GetFile(fi); + if (af == null) + { + continue; + } + + string outDirectory = Path.Combine(outDir.FullName, fi.Directory); + if (!Directory.Exists(outDirectory)) + { + Directory.CreateDirectory(outDirectory); + } + + string outPath = Path.Combine(outDirectory, fi.Name); + File.WriteAllBytes(outPath, af.Data); + } + + Logger.Info($"Processing {i}/{files.Length} {filePath}"); + } + } + + public void TexToDds(FileInfo fileInfo) + { + Texture texture = new Texture(); + texture.Open(fileInfo.FullName); + texture.SaveDds($"{fileInfo.FullName}.dds"); + + // Texture tex = ArcArchive.GetResource( + // romDirectory, + // "game_common.arc", + // "scr/sky/DDBaseCube4_CM", + // "tex" + // ); + // tex.SaveDds("E:/Games/ARCtool/DDBaseCube4_CM.tex.dds"); + // tex.Save("E:/Games/ARCtool/DDBaseCube4_CM.tex"); + + + // string p3 = "E:/Games/ARCtool/DefaultCube_CM.tex"; + // Texture t3 = new Texture(); + // t3.Open(p3); + // t3.SaveDds(p3 + ".dds"); + + + // string p0 = "E:/Games/Dragon's Dogma Online/nativePC/system/texture/sysfont_AM_NOMIP.tex"; + // Texture t0 = new Texture(); + // t0.Open(p0); + // t0.SaveDds("E:/Games/ARCtool/sysfont_AM_NOMIP.tex" + ".dds"); + + + // string p1 = "E:/Games/Dragon's Dogma Online/nativePC/system/texture/detail_sysfont_AM_NOMIP.tex"; + // Texture t1 = new Texture(); + // t1.Open(p1); + + + int i = 1; + } + public void Shutdown() { } diff --git a/Arrowgene.Ddon.Client/ArcArchive.cs b/Arrowgene.Ddon.Client/ArcArchive.cs index 0c5f6af0f..c6f4fac64 100644 --- a/Arrowgene.Ddon.Client/ArcArchive.cs +++ b/Arrowgene.Ddon.Client/ArcArchive.cs @@ -3,6 +3,7 @@ using System.IO; using System.Text; using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared; using Arrowgene.Ddon.Shared.Crypto; using Arrowgene.Logging; using ICSharpCode.SharpZipLib.Zip.Compression; @@ -31,7 +32,79 @@ private static void Register(string className, string extension) arcExt.JamCrcStr = $"0x{arcExt.JamCrc:X8}"; JamCrcLookup.Add(arcExt.JamCrc, arcExt); } + + public static T GetFile(DirectoryInfo romDir, string arcPath, string filePath, string ext = null) where T : ClientFile, new() + { + ArcFile arcFile = GetArcFile(romDir, arcPath, filePath, ext, true); + if (arcFile == null) + { + return null; + } + + T file = new T(); + file.Open(arcFile.Data); + return file; + } + + public static T GetResource(DirectoryInfo romDir, string arcPath, string filePath, string ext = null) where T : ResourceFile, new() + { + ArcFile arcFile = GetArcFile(romDir, arcPath, filePath, ext, true); + if (arcFile == null) + { + return null; + } + + T resource = new T(); + resource.Open(arcFile.Data); + return resource; + } + + public static T GetResource_NoLog(DirectoryInfo romDir, string arcPath, string filePath, string ext = null) where T : ResourceFile, new() + { + ArcFile arcFile = GetArcFile(romDir, arcPath, filePath, ext, false); + if (arcFile == null) + { + return null; + } + T resource = new T(); + resource.Open(arcFile.Data); + return resource; + } + + public static ArcFile GetArcFile(DirectoryInfo romDir, string arcPath, string filePath, string ext, bool log) + { + string path = Path.Combine(romDir.FullName, Util.UnrootPath(arcPath)); + FileInfo file = new FileInfo(path); + if (!file.Exists) + { + if (log) + { + Logger.Error($"File does not exist. ({path})"); + } + + return null; + } + + ArcArchive archive = new ArcArchive(); + archive.Open(file.FullName); + FileIndexSearch search = Search() + .ByArcPath(filePath) + .ByExtension(ext); + ArcFile arcFile = archive.GetFile(search); + if (arcFile == null) + { + if (log) + { + Logger.Error($"File:{filePath} could not be located in archive:{path}"); + } + + return null; + } + + return arcFile; + } + public static FileIndexSearch Search() { return new FileIndexSearch(); diff --git a/Arrowgene.Ddon.Client/ClientFile.cs b/Arrowgene.Ddon.Client/ClientFile.cs index 9c70d79f1..1f41a5e21 100644 --- a/Arrowgene.Ddon.Client/ClientFile.cs +++ b/Arrowgene.Ddon.Client/ClientFile.cs @@ -98,6 +98,11 @@ protected List ReadMtArray(IBuffer buffer, Func reader) return entities; } + protected byte[] ReadBytes(IBuffer buffer, int length) + { + return buffer.ReadBytes(length); + } + protected void WriteUInt32(IBuffer buffer, uint value) { buffer.WriteUInt32(value, Endianness.Little); diff --git a/Arrowgene.Ddon.Client/ClientResourceRepository.cs b/Arrowgene.Ddon.Client/ClientResourceRepository.cs index 09acf9854..41ae1fb57 100644 --- a/Arrowgene.Ddon.Client/ClientResourceRepository.cs +++ b/Arrowgene.Ddon.Client/ClientResourceRepository.cs @@ -221,74 +221,17 @@ private void AddAdjoin(List adjoins, private T GetFile(string arcPath, string filePath, string ext = null) where T : ClientFile, new() { - ArcArchive.ArcFile arcFile = GetArcFile(arcPath, filePath, ext, true); - if (arcFile == null) - { - return null; - } - - T file = new T(); - file.Open(arcFile.Data); - return file; + return ArcArchive.GetFile(_directory, arcPath, filePath, ext); } private T GetResource(string arcPath, string filePath, string ext = null) where T : ResourceFile, new() { - ArcArchive.ArcFile arcFile = GetArcFile(arcPath, filePath, ext, true); - if (arcFile == null) - { - return null; - } - - T resource = new T(); - resource.Open(arcFile.Data); - return resource; + return ArcArchive.GetResource(_directory, arcPath, filePath, ext); } private T GetResource_NoLog(string arcPath, string filePath, string ext = null) where T : ResourceFile, new() { - ArcArchive.ArcFile arcFile = GetArcFile(arcPath, filePath, ext, false); - if (arcFile == null) - { - return null; - } - - T resource = new T(); - resource.Open(arcFile.Data); - return resource; - } - - private ArcArchive.ArcFile GetArcFile(string arcPath, string filePath, string ext, bool log) - { - string path = Path.Combine(_directory.FullName, Util.UnrootPath(arcPath)); - FileInfo file = new FileInfo(path); - if (!file.Exists) - { - if (log) - { - Logger.Error($"File does not exist. ({path})"); - } - - return null; - } - - ArcArchive archive = new ArcArchive(); - archive.Open(file.FullName); - ArcArchive.FileIndexSearch search = ArcArchive.Search() - .ByArcPath(filePath) - .ByExtension(ext); - ArcArchive.ArcFile arcFile = archive.GetFile(search); - if (arcFile == null) - { - if (log) - { - Logger.Error($"File:{filePath} could not be located in archive:{path}"); - } - - return null; - } - - return arcFile; + return ArcArchive.GetResource_NoLog(_directory, arcPath, filePath, ext); } } } diff --git a/Arrowgene.Ddon.Client/DirectXTexUtility.cs b/Arrowgene.Ddon.Client/DirectXTexUtility.cs new file mode 100644 index 000000000..88dcc60e2 --- /dev/null +++ b/Arrowgene.Ddon.Client/DirectXTexUtility.cs @@ -0,0 +1,1185 @@ +// ------------------------------------------------------------------------ +// DirectXTex Utility - A simple class for generating DDS Headers +// Copyright(c) 2018 Philip/Scobalula +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// ------------------------------------------------------------------------ +// Author: Philip/Scobalula +// Description: DirectXTex DDS Header Utilities + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Arrowgene.Ddon.Client +{ + public class DirectXTexUtility + { + #region Enumerators + + /// + /// DDS Formats + /// + public enum DXGIFormat : uint + { + UNKNOWN = 0, + R32G32B32A32TYPELESS = 1, + R32G32B32A32FLOAT = 2, + R32G32B32A32UINT = 3, + R32G32B32A32SINT = 4, + R32G32B32TYPELESS = 5, + R32G32B32FLOAT = 6, + R32G32B32UINT = 7, + R32G32B32SINT = 8, + R16G16B16A16TYPELESS = 9, + R16G16B16A16FLOAT = 10, + R16G16B16A16UNORM = 11, + R16G16B16A16UINT = 12, + R16G16B16A16SNORM = 13, + R16G16B16A16SINT = 14, + R32G32TYPELESS = 15, + R32G32FLOAT = 16, + R32G32UINT = 17, + R32G32SINT = 18, + R32G8X24TYPELESS = 19, + D32FLOATS8X24UINT = 20, + R32FLOATX8X24TYPELESS = 21, + X32TYPELESSG8X24UINT = 22, + R10G10B10A2TYPELESS = 23, + R10G10B10A2UNORM = 24, + R10G10B10A2UINT = 25, + R11G11B10FLOAT = 26, + R8G8B8A8TYPELESS = 27, + R8G8B8A8UNORM = 28, + R8G8B8A8UNORMSRGB = 29, + R8G8B8A8UINT = 30, + R8G8B8A8SNORM = 31, + R8G8B8A8SINT = 32, + R16G16TYPELESS = 33, + R16G16FLOAT = 34, + R16G16UNORM = 35, + R16G16UINT = 36, + R16G16SNORM = 37, + R16G16SINT = 38, + R32TYPELESS = 39, + D32FLOAT = 40, + R32FLOAT = 41, + R32UINT = 42, + R32SINT = 43, + R24G8TYPELESS = 44, + D24UNORMS8UINT = 45, + R24UNORMX8TYPELESS = 46, + X24TYPELESSG8UINT = 47, + R8G8TYPELESS = 48, + R8G8UNORM = 49, + R8G8UINT = 50, + R8G8SNORM = 51, + R8G8SINT = 52, + R16TYPELESS = 53, + R16FLOAT = 54, + D16UNORM = 55, + R16UNORM = 56, + R16UINT = 57, + R16SNORM = 58, + R16SINT = 59, + R8TYPELESS = 60, + R8UNORM = 61, + R8UINT = 62, + R8SNORM = 63, + R8SINT = 64, + A8UNORM = 65, + R1UNORM = 66, + R9G9B9E5SHAREDEXP = 67, + R8G8B8G8UNORM = 68, + G8R8G8B8UNORM = 69, + BC1TYPELESS = 70, + BC1UNORM = 71, + BC1UNORMSRGB = 72, + BC2TYPELESS = 73, + BC2UNORM = 74, + BC2UNORMSRGB = 75, + BC3TYPELESS = 76, + BC3UNORM = 77, + BC3UNORMSRGB = 78, + BC4TYPELESS = 79, + BC4UNORM = 80, + BC4SNORM = 81, + BC5TYPELESS = 82, + BC5UNORM = 83, + BC5SNORM = 84, + B5G6R5UNORM = 85, + B5G5R5A1UNORM = 86, + B8G8R8A8UNORM = 87, + B8G8R8X8UNORM = 88, + R10G10B10XRBIASA2UNORM = 89, + B8G8R8A8TYPELESS = 90, + B8G8R8A8UNORMSRGB = 91, + B8G8R8X8TYPELESS = 92, + B8G8R8X8UNORMSRGB = 93, + BC6HTYPELESS = 94, + BC6HUF16 = 95, + BC6HSF16 = 96, + BC7TYPELESS = 97, + BC7UNORM = 98, + BC7UNORMSRGB = 99, + AYUV = 100, + Y410 = 101, + Y416 = 102, + NV12 = 103, + P010 = 104, + P016 = 105, + OPAQUE420 = 106, + YUY2 = 107, + Y210 = 108, + Y216 = 109, + NV11 = 110, + AI44 = 111, + IA44 = 112, + P8 = 113, + A8P8 = 114, + B4G4R4A4UNORM = 115, + FORCEUINT = 0xffffffff + } + + /// + /// DDS Flags + /// + public enum DDSFlags + { + NONE = 0x0, + LEGACYDWORD = 0x1, + NOLEGACYEXPANSION = 0x2, + NOR10B10G10A2FIXUP = 0x4, + FORCERGB = 0x8, + NO16BPP = 0x10, + EXPANDLUMINANCE = 0x20, + BADDXTNTAILS = 0x40, + FORCEDX10EXT = 0x10000, + FORCEDX10EXTMISC2 = 0x20000, + } + + /// + /// Texture Dimension + /// + public enum TexDimension + { + TEXTURE1D = 2, + TEXTURE2D = 3, + TEXTURE3D = 4, + } + + /// + /// Misc. Texture Flags + /// + public enum TexMiscFlags : uint + { + TEXTURECUBE = 0x4, + }; + + /// + /// Misc. Texture Flags + /// + public enum TexMiscFlags2 : uint + { + TEXMISC2ALPHAMODEMASK = 0x7, + }; + + /// + /// Texture Alpha Modes + /// + public enum TexAlphaMode + { + UNKNOWN = 0, + STRAIGHT = 1, + PREMULTIPLIED = 2, + OPAQUE = 3, + CUSTOM = 4, + }; + + /// + /// CP Flags + /// + public enum CPFLAGS + { + NONE = 0x0, // Normal operation + LEGACYDWORD = 0x1, // Assume pitch is DWORD aligned instead of BYTE aligned + PARAGRAPH = 0x2, // Assume pitch is 16-byte aligned instead of BYTE aligned + YMM = 0x4, // Assume pitch is 32-byte aligned instead of BYTE aligned + ZMM = 0x8, // Assume pitch is 64-byte aligned instead of BYTE aligned + PAGE4K = 0x200, // Assume pitch is 4096-byte aligned instead of BYTE aligned + BADDXTNTAILS = 0x1000, // BC formats with malformed mipchain blocks smaller than 4x4 + BPP24 = 0x10000, // Override with a legacy 24 bits-per-pixel format size + BPP16 = 0x20000, // Override with a legacy 16 bits-per-pixel format size + BPP8 = 0x40000, // Override with a legacy 8 bits-per-pixel format size + }; + + #endregion + + #region Structs/Classes + + /// + /// Common Pixel Formats + /// + public class PixelFormats + { + /// + /// DDS Pixel Format Size + /// + public static readonly uint Size = (uint) Marshal.SizeOf(); + + #region PixelFormatsConstants + + public const uint DDSFOURCC = 0x00000004; // DDPFFOURCC + public const uint DDSRGB = 0x00000040; // DDPFRGB + public const uint DDSRGBA = 0x00000041; // DDPFRGB | DDPFALPHAPIXELS + public const uint DDSLUMINANCE = 0x00020000; // DDPFLUMINANCE + public const uint DDSLUMINANCEA = 0x00020001; // DDPFLUMINANCE | DDPFALPHAPIXELS + public const uint DDSALPHAPIXELS = 0x00000001; // DDPFALPHAPIXELS + public const uint DDSALPHA = 0x00000002; // DDPFALPHA + public const uint DDSPAL8 = 0x00000020; // DDPFPALETTEINDEXED8 + public const uint DDSPAL8A = 0x00000021; // DDPFPALETTEINDEXED8 | DDPFALPHAPIXELS + public const uint DDSBUMPDUDV = 0x00080000; // DDPFBUMPDUDV + + #endregion + + #region DDSPixelFormats + + public static DDSHeader.DDSPixelFormat DXT1 = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '1'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat DXT2 = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '2'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat DXT3 = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '3'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat DXT4 = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '4'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat DXT5 = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', 'T', '5'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat BC4UNORM = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('B', 'C', '4', 'U'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat BC4SNORM = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('B', 'C', '4', 'S'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat BC5UNORM = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('B', 'C', '5', 'U'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat BC5SNORM = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('B', 'C', '5', 'S'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat R8G8B8G8 = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('R', 'G', 'B', 'G'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat G8R8G8B8 = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('G', 'R', 'G', 'B'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat YUY2 = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('Y', 'U', 'Y', '2'), 0, 0, 0, 0, 0); + + public static DDSHeader.DDSPixelFormat A8R8G8B8 = + new DDSHeader.DDSPixelFormat(Size, DDSRGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); + + public static DDSHeader.DDSPixelFormat X8R8G8B8 = + new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000); + + public static DDSHeader.DDSPixelFormat A8B8G8R8 = + new DDSHeader.DDSPixelFormat(Size, DDSRGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + + public static DDSHeader.DDSPixelFormat X8B8G8R8 = + new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000); + + public static DDSHeader.DDSPixelFormat G16R16 = + new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000); + + public static DDSHeader.DDSPixelFormat R5G6B5 = + new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000); + + public static DDSHeader.DDSPixelFormat A1R5G5B5 = + new DDSHeader.DDSPixelFormat(Size, DDSRGBA, 0, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000); + + public static DDSHeader.DDSPixelFormat A4R4G4B4 = + new DDSHeader.DDSPixelFormat(Size, DDSRGBA, 0, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000); + + public static DDSHeader.DDSPixelFormat R8G8B8 = + new DDSHeader.DDSPixelFormat(Size, DDSRGB, 0, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000); + + public static DDSHeader.DDSPixelFormat L8 = + new DDSHeader.DDSPixelFormat(Size, DDSLUMINANCE, 0, 8, 0xff, 0x00, 0x00, 0x00); + + public static DDSHeader.DDSPixelFormat L16 = + new DDSHeader.DDSPixelFormat(Size, DDSLUMINANCE, 0, 16, 0xffff, 0x0000, 0x0000, 0x0000); + + public static DDSHeader.DDSPixelFormat A8L8 = + new DDSHeader.DDSPixelFormat(Size, DDSLUMINANCEA, 0, 16, 0x00ff, 0x0000, 0x0000, 0xff00); + + public static DDSHeader.DDSPixelFormat A8L8ALT = + new DDSHeader.DDSPixelFormat(Size, DDSLUMINANCEA, 0, 8, 0x00ff, 0x0000, 0x0000, 0xff00); + + public static DDSHeader.DDSPixelFormat A8 = + new DDSHeader.DDSPixelFormat(Size, DDSALPHA, 0, 8, 0x00, 0x00, 0x00, 0xff); + + public static DDSHeader.DDSPixelFormat V8U8 = + new DDSHeader.DDSPixelFormat(Size, DDSBUMPDUDV, 0, 16, 0x00ff, 0xff00, 0x0000, 0x0000); + + public static DDSHeader.DDSPixelFormat Q8W8V8U8 = + new DDSHeader.DDSPixelFormat(Size, DDSBUMPDUDV, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + + public static DDSHeader.DDSPixelFormat V16U16 = + new DDSHeader.DDSPixelFormat(Size, DDSBUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000); + + public static DDSHeader.DDSPixelFormat DX10 = + new DDSHeader.DDSPixelFormat(Size, DDSFOURCC, MakePixelFormatFourCC('D', 'X', '1', '0'), 0, 0, 0, 0, 0); + + #endregion + } + + /// + /// DDS Header + /// + public struct DDSHeader + { + /// + /// DDS Header Flags + /// + public enum HeaderFlags : uint + { + TEXTURE = 0x00001007, // DDSDCAPS | DDSDHEIGHT | DDSDWIDTH | DDSDPIXELFORMAT + MIPMAP = 0x00020000, // DDSDMIPMAPCOUNT + VOLUME = 0x00800000, // DDSDDEPTH + PITCH = 0x00000008, // DDSDPITCH + LINEARSIZE = 0x00080000, // DDSDLINEARSIZE + } + + /// + /// DDS Surface Flags + /// + public enum SurfaceFlags : uint + { + TEXTURE = 0x00001000, // DDSCAPSTEXTURE + MIPMAP = 0x00400008, // DDSCAPSCOMPLEX | DDSCAPSMIPMAP + CUBEMAP = 0x00000008, // DDSCAPSCOMPLEX + } + + /// + /// DDS Magic/Four CC + /// + public const uint DDSMagic = 0x20534444; + + /// + /// DDS Pixel Format + /// + public struct DDSPixelFormat + { + public uint Size; + public uint Flags; + public uint FourCC; + public uint RGBBitCount; + public uint RBitMask; + public uint GBitMask; + public uint BBitMask; + public uint ABitMask; + + /// + /// Creates a new DDS Pixel Format + /// + public DDSPixelFormat(uint size, uint flags, uint fourCC, uint rgbBitCount, uint rBitMask, + uint gBitMask, uint bBitMask, uint aBitMask) + { + Size = size; + Flags = flags; + FourCC = fourCC; + RGBBitCount = rgbBitCount; + RBitMask = rBitMask; + GBitMask = gBitMask; + BBitMask = bBitMask; + ABitMask = aBitMask; + } + } + + public uint Size; + public HeaderFlags Flags; + public uint Height; + public uint Width; + public uint PitchOrLinearSize; + public uint Depth; // only if DDSHEADERFLAGSVOLUME is set in flags + public uint MipMapCount; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public uint[] Reserved1; + + public DDSPixelFormat PixelFormat; + public uint Caps; + public uint Caps2; + public uint Caps3; + public uint Caps4; + public uint Reserved2; + } + + /// + /// DDS DX10 Header + /// + public struct DX10Header + { + public DXGIFormat Format; + public TexDimension ResourceDimension; + public TexMiscFlags MiscFlag; // see D3D11RESOURCEMISCFLAG + public uint ArraySize; + public uint MiscFlags2; // see DDSMISCFLAGS2 + } + + /// + /// Texture Metadata + /// + public struct TexMetadata + { + #region Properties + + public long Width; + public long Height; // Should be 1 for 1D textures + public long Depth; // Should be 1 for 1D or 2D textures + public long ArraySize; // For cubemap, this is a multiple of 6 + public long MipLevels; + public TexMiscFlags MiscFlags; + public TexMiscFlags2 MiscFlags2; + public DXGIFormat Format; + public TexDimension Dimension; + + #endregion + + /// + /// Creates a new Texture Metadata Structe + /// + public TexMetadata(long width, long height, long depth, long arraySize, long mipLevels, TexMiscFlags flags, + TexMiscFlags2 flags2, DXGIFormat format, TexDimension dimension) + { + Width = width; + Height = height; + Depth = depth; + ArraySize = arraySize; + MipLevels = mipLevels; + MiscFlags = flags; + MiscFlags2 = flags2; + Format = format; + Dimension = dimension; + } + + /// + /// Checks Alpha Mode + /// + public bool IsPMAlpha() + { + return (TexAlphaMode) (MiscFlags2 & TexMiscFlags2.TEXMISC2ALPHAMODEMASK) == TexAlphaMode.PREMULTIPLIED; + } + + public bool IsCubeMap() + { + return (MiscFlags & TexMiscFlags.TEXTURECUBE) == TexMiscFlags.TEXTURECUBE; + } + } + + #endregion + + #region HelperMethods + + /// + /// Clamps Value to a range. + /// + /// Value to Clamp + /// Max value + /// Min value + /// Clamped Value + private static T Clamp(T value, T max, T min) where T : IComparable + { + return value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value; + } + + /// + /// Converts a Struct to a Byte array + /// + private static byte[] StructToBytes(T value) + { + // Size of Struct + int length = Marshal.SizeOf(); + // Destination + byte[] destination = new byte[length]; + // Get Pointer + IntPtr pointer = Marshal.AllocHGlobal(length); + // Convert it + Marshal.StructureToPtr(value, pointer, false); + Marshal.Copy(pointer, destination, 0, length); + Marshal.FreeHGlobal(pointer); + // Done + return destination; + } + + /// + /// Generates a FourCC Integer from Pixel Format Characters + /// + private static uint MakePixelFormatFourCC(char char1, char char2, char char3, char char4) + { + return Convert.ToByte(char1) | (uint) Convert.ToByte(char2) << 8 | (uint) Convert.ToByte(char3) << 16 | + (uint) Convert.ToByte(char4) << 24; + } + + /// + /// Gets the Bits Per Pixel for the given format + /// + private static long BitsPerPixel(DXGIFormat format) + { + switch (format) + { + case DXGIFormat.R32G32B32A32TYPELESS: + case DXGIFormat.R32G32B32A32FLOAT: + case DXGIFormat.R32G32B32A32UINT: + case DXGIFormat.R32G32B32A32SINT: + return 128; + case DXGIFormat.R32G32B32TYPELESS: + case DXGIFormat.R32G32B32FLOAT: + case DXGIFormat.R32G32B32UINT: + case DXGIFormat.R32G32B32SINT: + return 96; + case DXGIFormat.R16G16B16A16TYPELESS: + case DXGIFormat.R16G16B16A16FLOAT: + case DXGIFormat.R16G16B16A16UNORM: + case DXGIFormat.R16G16B16A16UINT: + case DXGIFormat.R16G16B16A16SNORM: + case DXGIFormat.R16G16B16A16SINT: + case DXGIFormat.R32G32TYPELESS: + case DXGIFormat.R32G32FLOAT: + case DXGIFormat.R32G32UINT: + case DXGIFormat.R32G32SINT: + case DXGIFormat.R32G8X24TYPELESS: + case DXGIFormat.D32FLOATS8X24UINT: + case DXGIFormat.R32FLOATX8X24TYPELESS: + case DXGIFormat.X32TYPELESSG8X24UINT: + case DXGIFormat.Y416: + case DXGIFormat.Y210: + case DXGIFormat.Y216: + return 64; + case DXGIFormat.R10G10B10A2TYPELESS: + case DXGIFormat.R10G10B10A2UNORM: + case DXGIFormat.R10G10B10A2UINT: + case DXGIFormat.R11G11B10FLOAT: + case DXGIFormat.R8G8B8A8TYPELESS: + case DXGIFormat.R8G8B8A8UNORM: + case DXGIFormat.R8G8B8A8UNORMSRGB: + case DXGIFormat.R8G8B8A8UINT: + case DXGIFormat.R8G8B8A8SNORM: + case DXGIFormat.R8G8B8A8SINT: + case DXGIFormat.R16G16TYPELESS: + case DXGIFormat.R16G16FLOAT: + case DXGIFormat.R16G16UNORM: + case DXGIFormat.R16G16UINT: + case DXGIFormat.R16G16SNORM: + case DXGIFormat.R16G16SINT: + case DXGIFormat.R32TYPELESS: + case DXGIFormat.D32FLOAT: + case DXGIFormat.R32FLOAT: + case DXGIFormat.R32UINT: + case DXGIFormat.R32SINT: + case DXGIFormat.R24G8TYPELESS: + case DXGIFormat.D24UNORMS8UINT: + case DXGIFormat.R24UNORMX8TYPELESS: + case DXGIFormat.X24TYPELESSG8UINT: + case DXGIFormat.R9G9B9E5SHAREDEXP: + case DXGIFormat.R8G8B8G8UNORM: + case DXGIFormat.G8R8G8B8UNORM: + case DXGIFormat.B8G8R8A8UNORM: + case DXGIFormat.B8G8R8X8UNORM: + case DXGIFormat.R10G10B10XRBIASA2UNORM: + case DXGIFormat.B8G8R8A8TYPELESS: + case DXGIFormat.B8G8R8A8UNORMSRGB: + case DXGIFormat.B8G8R8X8TYPELESS: + case DXGIFormat.B8G8R8X8UNORMSRGB: + case DXGIFormat.AYUV: + case DXGIFormat.Y410: + case DXGIFormat.YUY2: + return 32; + case DXGIFormat.P010: + case DXGIFormat.P016: + return 24; + case DXGIFormat.R8G8TYPELESS: + case DXGIFormat.R8G8UNORM: + case DXGIFormat.R8G8UINT: + case DXGIFormat.R8G8SNORM: + case DXGIFormat.R8G8SINT: + case DXGIFormat.R16TYPELESS: + case DXGIFormat.R16FLOAT: + case DXGIFormat.D16UNORM: + case DXGIFormat.R16UNORM: + case DXGIFormat.R16UINT: + case DXGIFormat.R16SNORM: + case DXGIFormat.R16SINT: + case DXGIFormat.B5G6R5UNORM: + case DXGIFormat.B5G5R5A1UNORM: + case DXGIFormat.A8P8: + case DXGIFormat.B4G4R4A4UNORM: + return 16; + case DXGIFormat.NV12: + case DXGIFormat.OPAQUE420: + case DXGIFormat.NV11: + return 12; + case DXGIFormat.R8TYPELESS: + case DXGIFormat.R8UNORM: + case DXGIFormat.R8UINT: + case DXGIFormat.R8SNORM: + case DXGIFormat.R8SINT: + case DXGIFormat.A8UNORM: + case DXGIFormat.AI44: + case DXGIFormat.IA44: + case DXGIFormat.P8: + return 8; + case DXGIFormat.R1UNORM: + return 1; + case DXGIFormat.BC1TYPELESS: + case DXGIFormat.BC1UNORM: + case DXGIFormat.BC1UNORMSRGB: + case DXGIFormat.BC4TYPELESS: + case DXGIFormat.BC4UNORM: + case DXGIFormat.BC4SNORM: + return 4; + case DXGIFormat.BC2TYPELESS: + case DXGIFormat.BC2UNORM: + case DXGIFormat.BC2UNORMSRGB: + case DXGIFormat.BC3TYPELESS: + case DXGIFormat.BC3UNORM: + case DXGIFormat.BC3UNORMSRGB: + case DXGIFormat.BC5TYPELESS: + case DXGIFormat.BC5UNORM: + case DXGIFormat.BC5SNORM: + case DXGIFormat.BC6HTYPELESS: + case DXGIFormat.BC6HUF16: + case DXGIFormat.BC6HSF16: + case DXGIFormat.BC7TYPELESS: + case DXGIFormat.BC7UNORM: + case DXGIFormat.BC7UNORMSRGB: + return 8; + default: + return 0; + } + } + + /// + /// Computes Row and Slice Pitch + /// + public static void ComputePitch(DXGIFormat format, long width, long height, out long rowPitch, + out long slicePitch, CPFLAGS flags) + { + switch (format) + { + case DXGIFormat.BC1TYPELESS: + case DXGIFormat.BC1UNORM: + case DXGIFormat.BC1UNORMSRGB: + case DXGIFormat.BC4TYPELESS: + case DXGIFormat.BC4UNORM: + case DXGIFormat.BC4SNORM: + { + if (flags.HasFlag(CPFLAGS.BADDXTNTAILS)) + { + long nbw = width >> 2; + long nbh = height >> 2; + rowPitch = Clamp(1, nbw * 8, Int64.MaxValue); + slicePitch = Clamp(1, rowPitch * nbh, Int64.MaxValue); + } + else + { + long nbw = Clamp(1, (width + 3) / 4, Int64.MaxValue); + long nbh = Clamp(1, (height + 3) / 4, Int64.MaxValue); + rowPitch = nbw * 8; + slicePitch = rowPitch * nbh; + } + } + break; + case DXGIFormat.BC2TYPELESS: + case DXGIFormat.BC2UNORM: + case DXGIFormat.BC2UNORMSRGB: + case DXGIFormat.BC3TYPELESS: + case DXGIFormat.BC3UNORM: + case DXGIFormat.BC3UNORMSRGB: + case DXGIFormat.BC5TYPELESS: + case DXGIFormat.BC5UNORM: + case DXGIFormat.BC5SNORM: + case DXGIFormat.BC6HTYPELESS: + case DXGIFormat.BC6HUF16: + case DXGIFormat.BC6HSF16: + case DXGIFormat.BC7TYPELESS: + case DXGIFormat.BC7UNORM: + case DXGIFormat.BC7UNORMSRGB: + { + if (flags.HasFlag(CPFLAGS.BADDXTNTAILS)) + { + long nbw = width >> 2; + long nbh = height >> 2; + rowPitch = Clamp(1, nbw * 16, Int64.MaxValue); + slicePitch = Clamp(1, rowPitch * nbh, Int64.MaxValue); + } + else + { + long nbw = Clamp(1, (width + 3) / 4, Int64.MaxValue); + long nbh = Clamp(1, (height + 3) / 4, Int64.MaxValue); + rowPitch = nbw * 16; + slicePitch = rowPitch * nbh; + } + } + break; + case DXGIFormat.R8G8B8G8UNORM: + case DXGIFormat.G8R8G8B8UNORM: + case DXGIFormat.YUY2: + rowPitch = ((width + 1) >> 1) * 4; + slicePitch = rowPitch * height; + break; + case DXGIFormat.Y210: + case DXGIFormat.Y216: + rowPitch = ((width + 1) >> 1) * 8; + slicePitch = rowPitch * height; + break; + + case DXGIFormat.NV12: + case DXGIFormat.OPAQUE420: + rowPitch = ((width + 1) >> 1) * 2; + slicePitch = rowPitch * (height + ((height + 1) >> 1)); + break; + + case DXGIFormat.P010: + case DXGIFormat.P016: + rowPitch = ((width + 1) >> 1) * 4; + slicePitch = rowPitch * (height + ((height + 1) >> 1)); + break; + case DXGIFormat.NV11: + rowPitch = ((width + 3) >> 2) * 4; + slicePitch = rowPitch * height * 2; + break; + default: + { + long bpp; + + if (flags.HasFlag(CPFLAGS.BPP24)) + bpp = 24; + else if (flags.HasFlag(CPFLAGS.BPP16)) + bpp = 16; + else if (flags.HasFlag(CPFLAGS.BPP8)) + bpp = 8; + else + bpp = BitsPerPixel(format); + + if (flags.HasFlag(CPFLAGS.LEGACYDWORD | CPFLAGS.PARAGRAPH | CPFLAGS.YMM | CPFLAGS.ZMM | + CPFLAGS.PAGE4K)) + { + if (flags.HasFlag(CPFLAGS.PAGE4K)) + { + rowPitch = ((width * bpp + 32767) / 32768) * 4096; + slicePitch = rowPitch * height; + } + else if (flags.HasFlag(CPFLAGS.ZMM)) + { + rowPitch = ((width * bpp + 511) / 512) * 64; + slicePitch = rowPitch * height; + } + else if (flags.HasFlag(CPFLAGS.YMM)) + { + rowPitch = ((width * bpp + 255) / 256) * 32; + slicePitch = rowPitch * height; + } + else if (flags.HasFlag(CPFLAGS.PARAGRAPH)) + { + rowPitch = ((width * bpp + 127) / 128) * 16; + slicePitch = rowPitch * height; + } + else // DWORD alignment + { + // Special computation for some incorrectly created DDS files based on + // legacy DirectDraw assumptions about pitch alignment + rowPitch = ((width * bpp + 31) / 32) * 4; + slicePitch = rowPitch * height; + } + } + else + { + // Default byte alignment + rowPitch = (width * bpp + 7) / 8; + slicePitch = rowPitch * height; + } + } + break; + } + } + + /// + /// Checks is the given format compressed + /// + private static bool IsCompressed(DXGIFormat format) + { + switch (format) + { + case DXGIFormat.BC1TYPELESS: + case DXGIFormat.BC1UNORM: + case DXGIFormat.BC1UNORMSRGB: + case DXGIFormat.BC2TYPELESS: + case DXGIFormat.BC2UNORM: + case DXGIFormat.BC2UNORMSRGB: + case DXGIFormat.BC3TYPELESS: + case DXGIFormat.BC3UNORM: + case DXGIFormat.BC3UNORMSRGB: + case DXGIFormat.BC4TYPELESS: + case DXGIFormat.BC4UNORM: + case DXGIFormat.BC4SNORM: + case DXGIFormat.BC5TYPELESS: + case DXGIFormat.BC5UNORM: + case DXGIFormat.BC5SNORM: + case DXGIFormat.BC6HTYPELESS: + case DXGIFormat.BC6HUF16: + case DXGIFormat.BC6HSF16: + case DXGIFormat.BC7TYPELESS: + case DXGIFormat.BC7UNORM: + case DXGIFormat.BC7UNORMSRGB: + return true; + + default: + return false; + } + } + + #endregion + + #region MainMethods + + /// + /// Encodes the DDS Header and if DX10, the DX10 Header + /// + /// DDS Header + /// DX10 Header + /// Resulting DDS File Header in bytes + public static byte[] EncodeDDSHeader(DDSHeader header, DX10Header dx10Header) + { + // Create stream + using (var output = new BinaryWriter(new MemoryStream())) + { + // Write DDS Magic + output.Write(DDSHeader.DDSMagic); + // Write Header + output.Write(StructToBytes(header)); + // Check for DX10 Header + if (header.PixelFormat.FourCC == PixelFormats.DX10.FourCC) + // Write Header + output.Write(StructToBytes(dx10Header)); + // Done + return ((MemoryStream) (output.BaseStream)).ToArray(); + } + } + + /// + /// Generates DirectXTex Meta Data + /// + /// Image Width + /// Image Height + /// Number of Mip Maps + /// Compression Format + /// Whether or not this is a cube map + /// Resulting TexMetaData Object + public static TexMetadata GenerateMataData(int width, int height, int mipMapLevels, DXGIFormat format, + bool isCubeMap) + { + // Create Texture MetaData + return new TexMetadata( + width, + height, + 1, + isCubeMap ? 6 : 1, + mipMapLevels, + isCubeMap ? TexMiscFlags.TEXTURECUBE : 0, + 0, + format, + TexDimension.TEXTURE2D + ); + } + + /// + /// Generates a DDS Header, and if requires, a DX10 Header + /// + /// Meta Data + /// Flags + /// DDS Header Output + /// DX10 Header Output + public static void GenerateDDSHeader(TexMetadata metaData, DDSFlags flags, out DDSHeader header, + out DX10Header dx10Header) + { + // Check array size + if (metaData.ArraySize > 1) + // Check if we have an array and whether we're cube maps/non-2D + if (metaData.ArraySize != 6 || metaData.Dimension != TexDimension.TEXTURE2D || !metaData.IsCubeMap()) + // Texture1D arrays, Texture2D arrays, and Cubemap arrays must be stored using 'DX10' extended header + flags |= DDSFlags.FORCEDX10EXT; + + // Check for DX10 Ext + if (flags.HasFlag(DDSFlags.FORCEDX10EXTMISC2)) + flags |= DDSFlags.FORCEDX10EXT; + // Create DDS Header + header = new DDSHeader + { + // Set Data + Size = (uint) Marshal.SizeOf(), + Flags = DDSHeader.HeaderFlags.TEXTURE, + Caps = (uint) DDSHeader.SurfaceFlags.TEXTURE, + PixelFormat = new DDSHeader.DDSPixelFormat(0, 0, 0, 0, 0, 0, 0, 0) + }; + // Create DX10 Header + dx10Header = new DX10Header(); + // Switch format + switch (metaData.Format) + { + case DXGIFormat.R8G8B8A8UNORM: + header.PixelFormat = PixelFormats.A8B8G8R8; + break; + case DXGIFormat.R16G16UNORM: + header.PixelFormat = PixelFormats.G16R16; + break; + case DXGIFormat.R8G8UNORM: + header.PixelFormat = PixelFormats.A8L8; + break; + case DXGIFormat.R16UNORM: + header.PixelFormat = PixelFormats.L16; + break; + case DXGIFormat.R8UNORM: + header.PixelFormat = PixelFormats.L8; + break; + case DXGIFormat.A8UNORM: + header.PixelFormat = PixelFormats.A8; + break; + case DXGIFormat.R8G8B8G8UNORM: + header.PixelFormat = PixelFormats.R8G8B8G8; + break; + case DXGIFormat.G8R8G8B8UNORM: + header.PixelFormat = PixelFormats.G8R8G8B8; + break; + case DXGIFormat.BC1UNORM: + header.PixelFormat = PixelFormats.DXT1; + break; + case DXGIFormat.BC2UNORM: + header.PixelFormat = metaData.IsPMAlpha() ? (PixelFormats.DXT2) : (PixelFormats.DXT3); + break; + case DXGIFormat.BC3UNORM: + header.PixelFormat = metaData.IsPMAlpha() ? (PixelFormats.DXT4) : (PixelFormats.DXT5); + break; + case DXGIFormat.BC4UNORM: + header.PixelFormat = PixelFormats.BC4UNORM; + break; + case DXGIFormat.BC4SNORM: + header.PixelFormat = PixelFormats.BC4SNORM; + break; + case DXGIFormat.BC5UNORM: + header.PixelFormat = PixelFormats.BC5UNORM; + break; + case DXGIFormat.BC5SNORM: + header.PixelFormat = PixelFormats.BC5SNORM; + break; + case DXGIFormat.B5G6R5UNORM: + header.PixelFormat = PixelFormats.R5G6B5; + break; + case DXGIFormat.B5G5R5A1UNORM: + header.PixelFormat = PixelFormats.A1R5G5B5; + break; + case DXGIFormat.R8G8SNORM: + header.PixelFormat = PixelFormats.V8U8; + break; + case DXGIFormat.R8G8B8A8SNORM: + header.PixelFormat = PixelFormats.Q8W8V8U8; + break; + case DXGIFormat.R16G16SNORM: + header.PixelFormat = PixelFormats.V16U16; + break; + case DXGIFormat.B8G8R8A8UNORM: + header.PixelFormat = PixelFormats.A8R8G8B8; + break; + case DXGIFormat.B8G8R8X8UNORM: + header.PixelFormat = PixelFormats.X8R8G8B8; + break; + case DXGIFormat.B4G4R4A4UNORM: + header.PixelFormat = PixelFormats.A4R4G4B4; + break; + case DXGIFormat.YUY2: + header.PixelFormat = PixelFormats.YUY2; + break; + // Legacy D3DX formats using D3DFMT enum value as FourCC + case DXGIFormat.R32G32B32A32FLOAT: + header.PixelFormat.Flags = PixelFormats.DDSFOURCC; + header.PixelFormat.FourCC = 116; // D3DFMTA32B32G32R32F + break; + case DXGIFormat.R16G16B16A16FLOAT: + header.PixelFormat.Flags = PixelFormats.DDSFOURCC; + header.PixelFormat.FourCC = 113; // D3DFMTA16B16G16R16F + break; + case DXGIFormat.R16G16B16A16UNORM: + header.PixelFormat.Flags = PixelFormats.DDSFOURCC; + header.PixelFormat.FourCC = 36; // D3DFMTA16B16G16R16 + break; + case DXGIFormat.R16G16B16A16SNORM: + header.PixelFormat.Flags = PixelFormats.DDSFOURCC; + header.PixelFormat.FourCC = 110; // D3DFMTQ16W16V16U16 + break; + case DXGIFormat.R32G32FLOAT: + header.PixelFormat.Flags = PixelFormats.DDSFOURCC; + header.PixelFormat.FourCC = 115; // D3DFMTG32R32F + break; + case DXGIFormat.R16G16FLOAT: + header.PixelFormat.Flags = PixelFormats.DDSFOURCC; + header.PixelFormat.FourCC = 112; // D3DFMTG16R16F + break; + case DXGIFormat.R32FLOAT: + header.PixelFormat.Flags = PixelFormats.DDSFOURCC; + header.PixelFormat.FourCC = 114; // D3DFMTR32F + break; + case DXGIFormat.R16FLOAT: + header.PixelFormat.Flags = PixelFormats.DDSFOURCC; + header.PixelFormat.FourCC = 111; // D3DFMTR16F + break; + default: + break; + } + + // Check for mips + if (metaData.MipLevels > 0) + { + // Set flag + header.Flags |= DDSHeader.HeaderFlags.MIPMAP; + // Check size + if (metaData.MipLevels > UInt16.MaxValue) + throw new ArgumentException(String.Format("Too many mipmaps: {0}. Max: {1}", metaData.MipLevels, + UInt16.MaxValue)); + // Set + header.MipMapCount = (uint) metaData.MipLevels; + // Check count + if (header.MipMapCount > 1) + header.Caps |= (uint) DDSHeader.SurfaceFlags.MIPMAP; + } + + // Switch Dimension + switch (metaData.Dimension) + { + case TexDimension.TEXTURE1D: + { + // Check size + if (metaData.Width > Int32.MaxValue) + throw new ArgumentException(String.Format("Image Width too large: {0}. Max: {1}", + metaData.Width, Int32.MaxValue)); + // Set + header.Width = (uint) metaData.Width; + header.Height = header.Depth = 1; + // Check size + break; + } + case TexDimension.TEXTURE2D: + { + // Check size + if (metaData.Width > Int32.MaxValue || metaData.Height > Int32.MaxValue) + throw new ArgumentException(String.Format( + "Image Width and/or Height too large: {0}x{1}. Max: {2}", + metaData.Width, + metaData.Height, + Int32.MaxValue)); + // Set + header.Width = (uint) metaData.Width; + header.Height = (uint) metaData.Height; + header.Depth = 1; + // Check size + break; + } + case TexDimension.TEXTURE3D: + { + // Check size + if (metaData.Width > Int32.MaxValue || metaData.Height > Int32.MaxValue) + throw new ArgumentException(String.Format( + "Image Width and/or Height too large: {0}x{1}. Max: {2}", + metaData.Width, + metaData.Height, + Int32.MaxValue)); + // Check size + if (metaData.Depth > UInt16.MaxValue) + throw new ArgumentException(String.Format("Image Depth too large: {0}. Max: {1}", + metaData.Depth, UInt16.MaxValue)); + // Set + header.Flags |= DDSHeader.HeaderFlags.VOLUME; + header.Caps2 |= 0x00200000; + header.Width = (uint) metaData.Width; + header.Height = (uint) metaData.Height; + header.Depth = (uint) metaData.Depth; + // Check size + break; + } + default: + throw new ArgumentException("Invalid Texture Dimension."); + } + + // Calculate the Pitch + ComputePitch(metaData.Format, metaData.Width, metaData.Height, out long rowPitch, out long slicePitch, + CPFLAGS.NONE); + // Validate results + if (slicePitch > UInt32.MaxValue || rowPitch > UInt32.MaxValue) + throw new ArgumentException( + "Failed to calculate row and/or slice pitch, values returned were too large"); + // Check is it compressed + if (IsCompressed(metaData.Format)) + { + header.Flags |= DDSHeader.HeaderFlags.LINEARSIZE; + header.PitchOrLinearSize = (uint) slicePitch; + } + else + { + header.Flags |= DDSHeader.HeaderFlags.PITCH; + header.PitchOrLinearSize = (uint) rowPitch; + } + + // Check for do we need to create the DX10 Header + if (header.PixelFormat.Size == 0) + { + // Check size + if (metaData.ArraySize > UInt16.MaxValue) + throw new ArgumentException(String.Format("Array Size too large: {0}. Max: {1}", metaData.ArraySize, + UInt16.MaxValue)); + // Set Pixel format + header.PixelFormat = PixelFormats.DX10; + // Set Data + dx10Header.Format = metaData.Format; + dx10Header.ResourceDimension = metaData.Dimension; + dx10Header.MiscFlag = metaData.MiscFlags & ~TexMiscFlags.TEXTURECUBE; + dx10Header.ArraySize = (uint) metaData.ArraySize; + // Check for Cube Maps + if (metaData.MiscFlags.HasFlag(TexMiscFlags.TEXTURECUBE)) + { + // Check array size, must be a multiple of 6 for cube maps + if ((metaData.ArraySize % 6) != 0) + throw new ArgumentException("Array size must be a multiple of 6"); + // Set Flag + dx10Header.MiscFlag |= TexMiscFlags.TEXTURECUBE; + dx10Header.ArraySize /= 6; + } + + // Check for mist flags + if (flags.HasFlag(DDSFlags.FORCEDX10EXTMISC2)) + // This was formerly 'reserved'. D3DX10 and D3DX11 will fail if this value is anything other than 0 + dx10Header.MiscFlags2 = (uint) metaData.MiscFlags2; + } + } + + #endregion + } +} diff --git a/Arrowgene.Ddon.Client/Resource/AreaList.cs b/Arrowgene.Ddon.Client/Resource/AreaList.cs index 32a4d15fc..2af7b4be6 100644 --- a/Arrowgene.Ddon.Client/Resource/AreaList.cs +++ b/Arrowgene.Ddon.Client/Resource/AreaList.cs @@ -19,10 +19,9 @@ public AreaList() AreaInfos = new List(); } - protected override MagicIdWidth IdWidth => MagicIdWidth.UInt; - protected override void ReadResource(IBuffer buffer) { + uint version = ReadUInt32(buffer); AreaInfos.Clear(); AreaInfos.AddRange(ReadMtArray(buffer, ReadEntry)); } diff --git a/Arrowgene.Ddon.Client/Resource/AreaStageList.cs b/Arrowgene.Ddon.Client/Resource/AreaStageList.cs index 6240f8916..48e8fa71a 100644 --- a/Arrowgene.Ddon.Client/Resource/AreaStageList.cs +++ b/Arrowgene.Ddon.Client/Resource/AreaStageList.cs @@ -18,10 +18,9 @@ public AreaStageList() AreaInfoStages = new List(); } - protected override MagicIdWidth IdWidth => MagicIdWidth.UInt; - protected override void ReadResource(IBuffer buffer) { + uint version = ReadUInt32(buffer); AreaInfoStages.Clear(); AreaInfoStages.AddRange(ReadMtArray(buffer, ReadEntry)); } diff --git a/Arrowgene.Ddon.Client/Resource/FieldAreaAdjoinList.cs b/Arrowgene.Ddon.Client/Resource/FieldAreaAdjoinList.cs index 2ee53d35c..30f4cc280 100644 --- a/Arrowgene.Ddon.Client/Resource/FieldAreaAdjoinList.cs +++ b/Arrowgene.Ddon.Client/Resource/FieldAreaAdjoinList.cs @@ -6,8 +6,6 @@ namespace Arrowgene.Ddon.Client.Resource; public class FieldAreaAdjoinList : ResourceFile { - protected override MagicIdWidth IdWidth => MagicIdWidth.UInt; - public class Vector3 { public MtVector3 Pos { get; set; } @@ -37,6 +35,7 @@ public FieldAreaAdjoinList() protected override void ReadResource(IBuffer buffer) { + uint version = ReadUInt32(buffer); AdjoinInfos.Clear(); uint unk = ReadUInt16(buffer); uint count = ReadUInt32(buffer); diff --git a/Arrowgene.Ddon.Client/Resource/FieldAreaList.cs b/Arrowgene.Ddon.Client/Resource/FieldAreaList.cs index c4b9bbb7c..a34e21f36 100644 --- a/Arrowgene.Ddon.Client/Resource/FieldAreaList.cs +++ b/Arrowgene.Ddon.Client/Resource/FieldAreaList.cs @@ -24,7 +24,7 @@ public FieldAreaInfo() BelongStageNoList = new List(); } } - + public static FieldAreaInfo Get(List fieldAreaInfos, ushort areaId, ushort landId, int stageNo) { foreach (FieldAreaInfo fai in fieldAreaInfos) @@ -51,10 +51,9 @@ public FieldAreaList() FieldAreaInfos = new List(); } - protected override MagicIdWidth IdWidth => MagicIdWidth.UInt; - protected override void ReadResource(IBuffer buffer) { + uint version = ReadUInt32(buffer); FieldAreaInfos.Clear(); List infos = ReadMtArray(buffer, ReadEntry); FieldAreaInfos.AddRange(infos); diff --git a/Arrowgene.Ddon.Client/Resource/FieldAreaMarkerInfo.cs b/Arrowgene.Ddon.Client/Resource/FieldAreaMarkerInfo.cs index d004e64c9..314a8cec7 100644 --- a/Arrowgene.Ddon.Client/Resource/FieldAreaMarkerInfo.cs +++ b/Arrowgene.Ddon.Client/Resource/FieldAreaMarkerInfo.cs @@ -6,8 +6,6 @@ namespace Arrowgene.Ddon.Client.Resource; public class FieldAreaMarkerInfo : ResourceFile { - protected override MagicIdWidth IdWidth => MagicIdWidth.UInt; - public class MarkerInfo { public MtVector3 Pos { get; set; } @@ -25,6 +23,7 @@ public FieldAreaMarkerInfo() protected override void ReadResource(IBuffer buffer) { + uint version = ReadUInt32(buffer); MarkerInfos.Clear(); uint unk = ReadUInt32(buffer); uint count = ReadUInt32(buffer); diff --git a/Arrowgene.Ddon.Client/Resource/GuiMessage.cs b/Arrowgene.Ddon.Client/Resource/GuiMessage.cs index 435f827c6..a9a398401 100644 --- a/Arrowgene.Ddon.Client/Resource/GuiMessage.cs +++ b/Arrowgene.Ddon.Client/Resource/GuiMessage.cs @@ -25,10 +25,9 @@ public GuiMessage() Entries = new List(); } - protected override MagicIdWidth IdWidth => MagicIdWidth.UInt; - protected override void ReadResource(IBuffer buffer) { + uint version = ReadUInt32(buffer); uint a = ReadUInt32(buffer); uint b = ReadUInt32(buffer); uint c = ReadUInt32(buffer); diff --git a/Arrowgene.Ddon.Client/Resource/LandListLal.cs b/Arrowgene.Ddon.Client/Resource/LandListLal.cs index 79a008c1e..e90493ff2 100644 --- a/Arrowgene.Ddon.Client/Resource/LandListLal.cs +++ b/Arrowgene.Ddon.Client/Resource/LandListLal.cs @@ -24,11 +24,10 @@ public LandListLal() { LandInfos = new List(); } - - protected override MagicIdWidth IdWidth => MagicIdWidth.UInt; - + protected override void ReadResource(IBuffer buffer) { + uint version = ReadUInt32(buffer); LandInfos.Clear(); List infos = ReadMtArray(buffer, ReadEntry); LandInfos.AddRange(infos); diff --git a/Arrowgene.Ddon.Client/Resource/LocationData.cs b/Arrowgene.Ddon.Client/Resource/LocationData.cs index bc62aeb56..eff07836c 100644 --- a/Arrowgene.Ddon.Client/Resource/LocationData.cs +++ b/Arrowgene.Ddon.Client/Resource/LocationData.cs @@ -26,11 +26,10 @@ public LocationData() { Entries = new List(); } - - protected override MagicIdWidth IdWidth => MagicIdWidth.UInt; - + protected override void ReadResource(IBuffer buffer) { + uint version = ReadUInt32(buffer); Entries.Clear(); uint count = ReadUInt32(buffer); for (int i = 0; i < count; i++) diff --git a/Arrowgene.Ddon.Client/Resource/MsgSet.cs b/Arrowgene.Ddon.Client/Resource/MsgSet.cs index a5e890326..c02ee173f 100644 --- a/Arrowgene.Ddon.Client/Resource/MsgSet.cs +++ b/Arrowgene.Ddon.Client/Resource/MsgSet.cs @@ -36,10 +36,9 @@ public MsgSet() MsgGroups = new List(); } - protected override MagicIdWidth IdWidth => MagicIdWidth.UShort; - protected override void ReadResource(IBuffer buffer) { + ushort version = ReadUInt16(buffer); MsgGroups.Clear(); uint countA = ReadUInt32(buffer); uint countB = ReadUInt32(buffer); diff --git a/Arrowgene.Ddon.Client/Resource/StageList.cs b/Arrowgene.Ddon.Client/Resource/StageList.cs index 0eae7f2a5..122129f56 100644 --- a/Arrowgene.Ddon.Client/Resource/StageList.cs +++ b/Arrowgene.Ddon.Client/Resource/StageList.cs @@ -22,10 +22,9 @@ public StageList() StageInfos = new List(); } - protected override MagicIdWidth IdWidth => MagicIdWidth.UInt; - protected override void ReadResource(IBuffer buffer) { + uint version = ReadUInt32(buffer); StageInfos.Clear(); List infos = ReadMtArray(buffer, ReadEntry); StageInfos.AddRange(infos); diff --git a/Arrowgene.Ddon.Client/Resource/Texture.cs b/Arrowgene.Ddon.Client/Resource/Texture.cs new file mode 100644 index 000000000..44de89e79 --- /dev/null +++ b/Arrowgene.Ddon.Client/Resource/Texture.cs @@ -0,0 +1,244 @@ +using System.IO; +using Arrowgene.Buffers; +using Arrowgene.Logging; + +namespace Arrowgene.Ddon.Client.Resource +{ + public class Texture : ResourceFile + { + private static readonly ILogger Logger = LogProvider.Logger(typeof(Texture)); + + public TexHeader Header; + public byte[] HeaderA { get; set; } + public byte[] HeaderB { get; set; } + public byte[] Data { get; set; } + + protected override void ReadResource(IBuffer buffer) + { + //uint fileSize = (uint) org.Length; + + uint header4 = ReadUInt32(buffer); + uint header8 = ReadUInt32(buffer); + uint header12 = ReadUInt32(buffer); + + Header = new TexHeader(); + + // int version = (int) (header4 & 0xfff); + // int alpha_flag = (int) ((header4 >> 12) & 0xfff); + // int shift = (int) ((header4 >> 24) & 0xf); + // int unk2 = (int) ((header4 >> 28) & 0xf); + // int unk4 = (int) ((header12 >> 16) & 0x1fff); + + uint t = header4 >> 0x18; + uint t1 = t & 0xF; + + uint a1 = header8 >> 0x6; + uint a2 = a1 & 0x1FFF; + Header.Width = a2 << (byte) t1; + + uint aa1 = header8 >> 0x13; + Header.Height = aa1 << (byte) t1; + + uint aa2 = header12 >> 0x10; + uint aa3 = aa2 & 0x1FFF; + Header.Depth = aa3 << (byte) t1; + + Header.PixelFormat = (TexPixelFormat) ((header12 >> (1 * 8)) & 0xFF); + + if ((header4 & 0xF0000000) == 0x60000000) + { + HeaderA = ReadBytes(buffer, 0x6C); // &this->mSHFactor, + } + + Header.TextureArraySize = (byte) (header12 & 0xFF); + Header.MipMapCount = header8 & 0x3F; + uint layerCount = Header.TextureArraySize * Header.MipMapCount; // layer count + // uint ab111 = ab11 << 0x2; + + uint readCount = 4 * layerCount; + HeaderB = ReadBytes(buffer, (int) readCount); + + // uint v19 = aa1; + // if (a2 >= aa1) + // { + // v19 = a2; + // } + // uint mipLevelCount = CalcMipLevelCount(v19); + // uint switchNum = MagicId >> 0x1C; + // switch (switchNum) + // { + // case 1: + // case 2: + // break; + // case 3: + // break; + // case 6: + // break; + // } + Data = buffer.ReadBytes(buffer.Size - buffer.Position); + } + + public void SaveDds(string path) + { + GenerateDdsHeader(out DirectXTexUtility.DDSHeader ddsHeader, out DirectXTexUtility.DX10Header dx10Header); + byte[] ddsFileHeader = DirectXTexUtility.EncodeDDSHeader(ddsHeader, dx10Header); + StreamBuffer sb = new StreamBuffer(); + sb.WriteBytes(ddsFileHeader); + sb.WriteBytes(Data); + File.WriteAllBytes(path, sb.GetAllBytes()); + } + + private void GenerateDdsHeader(out DirectXTexUtility.DDSHeader ddsHeader, + out DirectXTexUtility.DX10Header dx10Header) + { + ddsHeader = new DirectXTexUtility.DDSHeader(); + ddsHeader.Size = 0x7C; //(uint) Marshal.SizeOf(); + ddsHeader.MipMapCount = Header.MipMapCount; + ddsHeader.Height = Header.Height; + ddsHeader.Width = Header.Width; + ddsHeader.Depth = Header.Depth; + ddsHeader.PixelFormat = ToDdsPixelFormat(Header.PixelFormat); + if (Header.MipMapCount > 0) + { + ddsHeader.Flags |= DirectXTexUtility.DDSHeader.HeaderFlags.MIPMAP; + ddsHeader.Caps |= (uint) DirectXTexUtility.DDSHeader.SurfaceFlags.MIPMAP; + } + + dx10Header = new DirectXTexUtility.DX10Header(); + if (Header.TextureArraySize > 1) + { + ddsHeader.PixelFormat = DirectXTexUtility.PixelFormats.DX10; + dx10Header.Format = ToDxGiFormat(Header.PixelFormat); + dx10Header.ResourceDimension = DirectXTexUtility.TexDimension.TEXTURE2D; + dx10Header.ArraySize = Header.TextureArraySize; + if (true) // TODO differentiate between cubemap and texture array + { + dx10Header.MiscFlag |= DirectXTexUtility.TexMiscFlags.TEXTURECUBE; + } + } + } + + private DirectXTexUtility.DDSHeader.DDSPixelFormat ToDdsPixelFormat(TexPixelFormat texPixelFormat) + { + // switch (type) + // { + // case 20: + // ddsHeader.PixelFormat = DirectXTexUtility.PixelFormats.DXT1; + // break; + // case 24: + // ddsHeader.PixelFormat = DirectXTexUtility.PixelFormats.DXT5; + // break; + // case 25: + // ddsHeader.PixelFormat = DirectXTexUtility.PixelFormats.DXT1; + // break; + // case 31: + // ddsHeader.PixelFormat = DirectXTexUtility.PixelFormats.DXT5; + // break; + // case 47: + // ddsHeader.PixelFormat = DirectXTexUtility.PixelFormats.DXT5; + // break; + // default: + // break; + // } + + switch (texPixelFormat) + { + case TexPixelFormat.FORMAT_BC1_UNORM_SRGB: return DirectXTexUtility.PixelFormats.DXT1; + case TexPixelFormat.FORMAT_BCX_RGBI_SRGB: return DirectXTexUtility.PixelFormats.DXT5; + default: + Logger.Error($"ToDdsPixelFormat::TexPixelFormat:{texPixelFormat} not handled"); + return DirectXTexUtility.PixelFormats.DXT1; + } + } + + private DirectXTexUtility.DXGIFormat ToDxGiFormat(TexPixelFormat texPixelFormat) + { + switch (texPixelFormat) + { + case TexPixelFormat.FORMAT_BC1_UNORM_SRGB: return DirectXTexUtility.DXGIFormat.BC1UNORMSRGB; + case TexPixelFormat.FORMAT_BCX_RGBI_SRGB: return DirectXTexUtility.DXGIFormat.BC3UNORMSRGB; + default: + Logger.Error($"ToDxGiFormat::TexPixelFormat:{texPixelFormat} not handled"); + return DirectXTexUtility.DXGIFormat.UNKNOWN; + } + } + + private uint CalcMipLevelCount(uint size) + { + uint v1 = 0xFFFFFFFF; + do + ++v1; + while (1 << (int) v1 <= size); + return v1; + } + + public struct TexHeader + { + public uint Height; + public uint Width; + public uint Depth; + public TexPixelFormat PixelFormat; + public byte TextureArraySize; + public uint MipMapCount; + } + + public enum TexPixelFormat + { + FORMAT_UNKNOWN = 0, + FORMAT_R32G32B32A32_FLOAT = 1, + FORMAT_R16G16B16A16_FLOAT = 2, + FORMAT_R16G16B16A16_UNORM = 3, + FORMAT_R16G16B16A16_SNORM = 4, + FORMAT_R32G32_FLOAT = 5, + FORMAT_R10G10B10A2_UNORM = 6, + FORMAT_R8G8B8A8_UNORM = 7, + FORMAT_R8G8B8A8_SNORM = 8, + FORMAT_R8G8B8A8_UNORM_SRGB = 9, + FORMAT_B4G4R4A4_UNORM = 0x0A, + FORMAT_R16G16_FLOAT = 0x0B, + FORMAT_R16G16_UNORM = 0x0C, + FORMAT_R16G16_SNORM = 0x0D, + FORMAT_R32_FLOAT = 0x0E, + FORMAT_D24_UNORM_S8_UINT = 0x0F, + FORMAT_R16_FLOAT = 0x10, + FORMAT_R16_UNORM = 0x11, + FORMAT_A8_UNORM = 0x12, + FORMAT_BC1_UNORM = 0x13, + FORMAT_BC1_UNORM_SRGB = 0x14, + FORMAT_BC2_UNORM = 0x15, + FORMAT_BC2_UNORM_SRGB = 0x16, + FORMAT_BC3_UNORM = 0x17, + FORMAT_BC3_UNORM_SRGB = 0x18, + FORMAT_BCX_GRAYSCALE = 0x19, + FORMAT_BCX_ALPHA = 0x1A, + FORMAT_BC5_SNORM = 0x1B, + FORMAT_B5G6R5_UNORM = 0x1C, + FORMAT_B5G5R5A1_UNORM = 0x1D, + FORMAT_BCX_NM1 = 0x1E, + FORMAT_BCX_NM2 = 0x1F, + FORMAT_BCX_RGBI = 0x20, + FORMAT_BCX_RGBY = 0x21, + FORMAT_B8G8R8X8_UNORM = 0x22, + FORMAT_BCX_RGBI_SRGB = 0x23, + FORMAT_BCX_RGBY_SRGB = 0x24, + FORMAT_BCX_NH = 0x25, + FORMAT_R11G11B10_FLOAT = 0x26, + FORMAT_B8G8R8A8_UNORM = 0x27, + FORMAT_B8G8R8A8_UNORM_SRGB = 0x28, + FORMAT_BCX_RGBNL = 0x29, + FORMAT_BCX_YCCA = 0x2A, + FORMAT_BCX_YCCA_SRGB = 0x2B, + FORMAT_R8_UNORM = 0x2C, + FORMAT_B8G8R8A8_UNORM_LE = 0x2D, + FORMAT_B10G10R10A2_UNORM_LE = 0x2E, + FORMAT_BCX_SRGBA = 0x2F, + FORMAT_BC7_UNORM = 0x30, + FORMAT_BC7_UNORM_SRGB = 0x31, + FORMAT_SE5M9M9M9 = 0x32, + FORMAT_R10G10B10A2_FLOAT = 0x33, + FORMAT_YVU420P2_CSC1 = 0x34, + FORMAT_R8A8_UNORM = 0x35, + FORMAT_A8_UNORM_WHITE = 0x36 + } + } +} diff --git a/Arrowgene.Ddon.Client/Resource/WarpLocationList.cs b/Arrowgene.Ddon.Client/Resource/WarpLocationList.cs index c597fd87b..c13751f6d 100644 --- a/Arrowgene.Ddon.Client/Resource/WarpLocationList.cs +++ b/Arrowgene.Ddon.Client/Resource/WarpLocationList.cs @@ -24,9 +24,7 @@ public WarpLocationList() { Entries = new List(); } - - protected override MagicIdWidth IdWidth => MagicIdWidth.Zero; - + protected override void ReadResource(IBuffer buffer) { Entries.Clear(); diff --git a/Arrowgene.Ddon.Client/ResourceFile.cs b/Arrowgene.Ddon.Client/ResourceFile.cs index 1a693b8d8..41ddc3b83 100644 --- a/Arrowgene.Ddon.Client/ResourceFile.cs +++ b/Arrowgene.Ddon.Client/ResourceFile.cs @@ -1,5 +1,4 @@ -using System; -using System.Text; +using System.Text; using Arrowgene.Buffers; using Arrowgene.Logging; @@ -7,19 +6,12 @@ namespace Arrowgene.Ddon.Client { public abstract class ResourceFile : ClientFile { - public enum MagicIdWidth - { - Zero, - UShort, - UInt - } - private static readonly ILogger Logger = LogProvider.Logger(typeof(ResourceFile)); - public string MagicTag { get; set; } - public uint MagicId { get; set; } + public string Magic { get; set; } - protected abstract MagicIdWidth IdWidth { get; } + // TODO Magic Validation + // protected abstract string ExpectedMagic { get; } protected override void Read(IBuffer buffer) { @@ -30,26 +22,8 @@ protected override void Read(IBuffer buffer) } byte[] magicTag = buffer.ReadBytes(4); - MagicTag = Encoding.UTF8.GetString(magicTag); - switch (IdWidth) - { - case MagicIdWidth.Zero: - { - MagicId = BitConverter.ToUInt32(magicTag); - break; - } - case MagicIdWidth.UShort: - { - MagicId = buffer.ReadUInt16(Endianness.Little); - break; - } - case MagicIdWidth.UInt: - { - MagicId = buffer.ReadUInt32(Endianness.Little); - break; - } - } - + Magic = Encoding.UTF8.GetString(magicTag); + // TODO Magic Validation ReadResource(buffer); if (buffer.Position != buffer.Size) { diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestGetMainQuestListHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestGetMainQuestListHandler.cs index b6f42f341..faecd304a 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestGetMainQuestListHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestGetMainQuestListHandler.cs @@ -1,6 +1,7 @@ using Arrowgene.Ddon.GameServer.Dump; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Server.Network; +using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; @@ -19,7 +20,17 @@ public QuestGetMainQuestListHandler(DdonGameServer server) : base(server) public override void Handle(GameClient client, IPacket packet) { - client.Send(GameFull.Dump_123); + client.Send(GameFull.Dump_123); + S2CQuestGetMainQuestListRes res = new S2CQuestGetMainQuestListRes(); + res.KeyId = 1; + res.QuestScheduleId = 1; + res.QuestId = 1; + res.NameMsgId = 1; + res.DetailMsgId = 2; + res.OrderNpcId = 1; + res.BaseLevel = 15; + + // client.Send(res); } } } diff --git a/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs b/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs index ab3e93517..f3cf0fb00 100644 --- a/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs +++ b/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs @@ -56,6 +56,7 @@ static EntitySerializer() Create(new CDataContextNormalSkillData.Serializer()); Create(new CDataContextPlayerInfo.Serializer()); Create(new CDataDeliveredItemRecord.Serializer()); + Create(new CDataDeliveryItem.Serializer()); Create(new CDataDropItemSetInfo.Serializer()); Create(new CDataEditInfoSerializer()); Create(new CDataEquipElementParam.Serializer()); @@ -227,6 +228,7 @@ static EntitySerializer() Create(new S2CPawn_8_37_16_Ntc.Serializer()); Create(new S2CPawnGetMypawnDataRes.Serializer()); Create(new S2CPawnJoinPartyMypawnRes.Serializer()); + Create(new S2CQuestGetMainQuestListRes.Serializer()); Create(new S2CQuestGetPartyQuestProgressInfoRes.Serializer()); Create(new S2CServerGameTimeGetBaseInfoRes.Serializer()); Create(new S2CServerGetRealTimeRes.Serializer()); diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CQuestGetMainQuestListRes.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CQuestGetMainQuestListRes.cs new file mode 100644 index 000000000..9fd368f16 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CQuestGetMainQuestListRes.cs @@ -0,0 +1,113 @@ +using System.Collections.Generic; +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Network; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class S2CQuestGetMainQuestListRes : ServerResponse + { + public override PacketId Id => PacketId.S2C_QUEST_GET_MAIN_QUEST_LIST_RES; + + public S2CQuestGetMainQuestListRes() + { + FixedRewardItemList = new List(); + FixedRewardSelectItemList = new List(); + QuestOrderConditionParamList = new List(); + QuestAnnounceList = new List(); + QuestTalkInfoList = new List(); + QuestFlagList = new List(); + QuestLayoutFlagList = new List(); + QuestProcessStateList = new List(); + QuestEnemyInfoList = new List(); + QuestLayoutFlagSetInfoList = new List(); + DeliveryItemList = new List(); + } + + public uint KeyId { get; set; } + public uint QuestScheduleId { get; set; } + public uint QuestId { get; set; } + public uint BaseLevel { get; set; } + public ushort ContentJoinItemRank { get; set; } + public uint BaseGold { get; set; } + public uint BaseExp { get; set; } + public uint BaseRim { get; set; } + public uint OrderNpcId { get; set; } + public uint NameMsgId { get; set; } + public uint DetailMsgId { get; set; } + public ulong EndDistributionDate { get; set; } + public List FixedRewardItemList { get; set; } + public List FixedRewardSelectItemList { get; set; } + public List QuestOrderConditionParamList { get; set; } + public List QuestAnnounceList { get; set; } + public List QuestTalkInfoList { get; set; } + public List QuestFlagList { get; set; } + public List QuestLayoutFlagList { get; set; } + public List QuestProcessStateList { get; set; } + public List QuestEnemyInfoList { get; set; } + public List QuestLayoutFlagSetInfoList { get; set; } + public List DeliveryItemList { get; set; } + + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, S2CQuestGetMainQuestListRes obj) + { + WriteServerResponse(buffer, obj); + WriteUInt32(buffer, obj.KeyId); + WriteUInt32(buffer, obj.QuestScheduleId); + WriteUInt32(buffer, obj.QuestId); + WriteUInt32(buffer, obj.BaseLevel); + WriteUInt16(buffer, obj.ContentJoinItemRank); + WriteUInt32(buffer, obj.BaseGold); + WriteUInt32(buffer, obj.BaseExp); + WriteUInt32(buffer, obj.BaseRim); + WriteUInt32(buffer, obj.OrderNpcId); + WriteUInt32(buffer, obj.NameMsgId); + WriteUInt32(buffer, obj.DetailMsgId); + WriteUInt64(buffer, obj.EndDistributionDate); + WriteEntityList(buffer, obj.FixedRewardItemList); + WriteEntityList(buffer, obj.FixedRewardSelectItemList); + WriteEntityList(buffer, obj.QuestOrderConditionParamList); + WriteEntityList(buffer, obj.QuestAnnounceList); + WriteEntityList(buffer, obj.QuestTalkInfoList); + WriteEntityList(buffer, obj.QuestFlagList); + WriteEntityList(buffer, obj.QuestLayoutFlagList); + WriteEntityList(buffer, obj.QuestProcessStateList); + WriteEntityList(buffer, obj.QuestEnemyInfoList); + WriteEntityList(buffer, obj.QuestLayoutFlagSetInfoList); + WriteEntityList(buffer, obj.DeliveryItemList); + } + + public override S2CQuestGetMainQuestListRes Read(IBuffer buffer) + { + S2CQuestGetMainQuestListRes obj = new S2CQuestGetMainQuestListRes(); + ReadServerResponse(buffer, obj); + obj.KeyId = buffer.ReadUInt32(Endianness.Big); + obj.QuestScheduleId = buffer.ReadUInt32(Endianness.Big); + obj.QuestId = buffer.ReadUInt32(Endianness.Big); + obj.BaseLevel = buffer.ReadUInt32(Endianness.Big); + obj.ContentJoinItemRank = buffer.ReadUInt16(Endianness.Big); + obj.BaseGold = buffer.ReadUInt32(Endianness.Big); + obj.BaseExp = buffer.ReadUInt32(Endianness.Big); + obj.BaseRim = buffer.ReadUInt32(Endianness.Big); + obj.OrderNpcId = buffer.ReadUInt32(Endianness.Big); + obj.NameMsgId = buffer.ReadUInt32(Endianness.Big); + obj.DetailMsgId = buffer.ReadUInt32(Endianness.Big); + obj.EndDistributionDate = buffer.ReadUInt64(Endianness.Big); + obj.FixedRewardItemList = ReadEntityList(buffer); + obj.FixedRewardSelectItemList = ReadEntityList(buffer); + obj.QuestOrderConditionParamList = ReadEntityList(buffer); + obj.QuestAnnounceList = ReadEntityList(buffer); + obj.QuestTalkInfoList = ReadEntityList(buffer); + obj.QuestFlagList = ReadEntityList(buffer); + obj.QuestLayoutFlagList = ReadEntityList(buffer); + obj.QuestProcessStateList = ReadEntityList(buffer); + obj.QuestEnemyInfoList = ReadEntityList(buffer); + obj.QuestLayoutFlagSetInfoList = ReadEntityList(buffer); + obj.DeliveryItemList = ReadEntityList(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/Structure/CDataDeliveryItem.cs b/Arrowgene.Ddon.Shared/Entity/Structure/CDataDeliveryItem.cs new file mode 100644 index 000000000..c3c563dba --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/Structure/CDataDeliveryItem.cs @@ -0,0 +1,25 @@ +using Arrowgene.Buffers; + +namespace Arrowgene.Ddon.Shared.Entity.Structure; + +public class CDataDeliveryItem +{ + public CDataDeliveryItem() + { + + } + + public class Serializer : EntitySerializer + { + public override void Write(IBuffer buffer, CDataDeliveryItem obj) + { + + } + + public override CDataDeliveryItem Read(IBuffer buffer) + { + CDataDeliveryItem obj = new CDataDeliveryItem(); + return obj; + } + } +} diff --git a/ReleaseFiles/tex2dds.cmd b/ReleaseFiles/tex2dds.cmd new file mode 100644 index 000000000..b2ce5dfcc --- /dev/null +++ b/ReleaseFiles/tex2dds.cmd @@ -0,0 +1,3 @@ +pushd "%~dp0" +cd ./Server. +start Arrowgene.Ddon.Cli.exe client %1 \ No newline at end of file