summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--NW4RTools/BrresReader.cs44
-rw-r--r--NW4RTools/Enums.cs15
-rw-r--r--NW4RTools/Misc.cs21
-rw-r--r--NW4RTools/NW4RTools.csproj3
-rw-r--r--NW4RTools/Texture.cs289
-rwxr-xr-xNW4RTools/bin/Debug/NW4RTools.dllbin159232 -> 162816 bytes
-rw-r--r--NW4RTools/bin/Debug/NW4RTools.dll.mdbbin87942 -> 89888 bytes
-rw-r--r--TestApp/Main.cs9
-rwxr-xr-xTestApp/bin/Debug/NW4RTools.dllbin159232 -> 162816 bytes
-rw-r--r--TestApp/bin/Debug/NW4RTools.dll.mdbbin87942 -> 89888 bytes
-rwxr-xr-xTestApp/bin/Debug/TestApp.exebin4096 -> 4608 bytes
-rw-r--r--TestApp/bin/Debug/TestApp.exe.mdbbin479 -> 548 bytes
12 files changed, 380 insertions, 1 deletions
diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs
index 20d7739..71dacc3 100644
--- a/NW4RTools/BrresReader.cs
+++ b/NW4RTools/BrresReader.cs
@@ -64,6 +64,9 @@ namespace NW4RTools {
case "3DModels(NW4R)":
File.Add(name, ReadAndConvertDict<Model>(ins, ConvertModelResource));
break;
+ case "Textures(NW4R)":
+ File.Add(name, ReadAndConvertDict<Texture>(ins, ConvertTextureResource));
+ break;
default:
Debug.Send("Not implemented");
return;
@@ -71,6 +74,47 @@ namespace NW4RTools {
}
}
+
+
+ private Texture ConvertTextureResource(string name, InputStream ins) {
+ using (var c = Debug.Push(name)) {
+ Texture tex = new Texture();
+
+ int startPos = ins.Position;
+
+ OffsetMap.Add(startPos, "Texture: " + name);
+
+ byte[] magic = ins.ReadBytes(4);
+ UInt32 size = ins.ReadUInt32();
+ UInt32 version = ins.ReadUInt32();
+
+ Debug.Send("Offset: 0x{0:X}; Size: 0x{1:X}; Version: {2}", startPos, size, version);
+
+ Int32 resFileOffset = ins.ReadInt32();
+ Int32 dataOffset = ins.ReadInt32();
+ Int32 nameOffset = ins.ReadInt32();
+
+ // Flags stores nothing interesting, just "is CI" flag (0x1)
+ UInt32 flags = ins.ReadUInt32();
+
+ Int16 width = ins.ReadInt16();
+ Int16 height = ins.ReadInt16();
+ TextureFormat format = (TextureFormat)ins.ReadUInt32();
+
+ tex.MipMapCount = ins.ReadUInt32();
+ tex.MinLOD = ins.ReadFloat();
+ tex.MaxLOD = ins.ReadFloat();
+
+ ins.Seek(startPos + dataOffset);
+ OffsetMap.Add(ins.Position, String.Format("Texture Data for: {0} [{1}, {2}x{3}]", name, format, width, height));
+ tex.ImportData(ins.ReadBytes(Texture.GetDataSize(width, height, format)), width, height, format);
+
+ return tex;
+ }
+ }
+
+
+
private Model ConvertModelResource(string name, InputStream ins) {
using (var c = Debug.Push(name)) {
Model mdl = new Model();
diff --git a/NW4RTools/Enums.cs b/NW4RTools/Enums.cs
index 21b6e8d..0b3518b 100644
--- a/NW4RTools/Enums.cs
+++ b/NW4RTools/Enums.cs
@@ -10,4 +10,19 @@ namespace NW4RTools {
LineStrip = 6,
Points = 7
}
+
+
+ public enum TextureFormat {
+ I4,
+ I8,
+ IA4,
+ IA8,
+ RGB565,
+ RGB5A3,
+ RGBA8,
+ C4 = 8,
+ C8 = 9,
+ C14X2 = 0xA,
+ CMPR = 0xE
+ }
}
diff --git a/NW4RTools/Misc.cs b/NW4RTools/Misc.cs
new file mode 100644
index 0000000..c42b185
--- /dev/null
+++ b/NW4RTools/Misc.cs
@@ -0,0 +1,21 @@
+using System;
+namespace NW4RTools {
+ public static class Misc {
+ public static int AlignUp(int val, int to) {
+ return (val + (to - 1)) & ~(to - 1);
+ }
+
+ public static uint AlignUp(uint val, uint to) {
+ return (val + (to - 1)) & ~(to - 1);
+ }
+
+ public static int AlignDown(int val, int to) {
+ return val & ~(to - 1);
+ }
+
+ public static uint AlignDown(uint val, uint to) {
+ return val & ~(to - 1);
+ }
+ }
+}
+
diff --git a/NW4RTools/NW4RTools.csproj b/NW4RTools/NW4RTools.csproj
index 6e4bc3e..0a462a3 100644
--- a/NW4RTools/NW4RTools.csproj
+++ b/NW4RTools/NW4RTools.csproj
@@ -34,6 +34,7 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
+ <Reference Include="System.Drawing" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
@@ -59,6 +60,8 @@
<Compile Include="VertexSettings.cs" />
<Compile Include="Util\collada_schema_1_4.cs" />
<Compile Include="ColladaWriter.cs" />
+ <Compile Include="Texture.cs" />
+ <Compile Include="Misc.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
diff --git a/NW4RTools/Texture.cs b/NW4RTools/Texture.cs
new file mode 100644
index 0000000..954852d
--- /dev/null
+++ b/NW4RTools/Texture.cs
@@ -0,0 +1,289 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace NW4RTools {
+ public class Texture {
+ public struct TextureFormatInfo {
+ public int TexelWidth, TexelHeight, NybblesPerPixel;
+
+ public const int TexelByteSize = 32;
+
+ private TextureFormatInfo(int tWidth, int tHeight, int npp) {
+ TexelWidth = tWidth;
+ TexelHeight = tHeight;
+ NybblesPerPixel = npp;
+ }
+
+ static TextureFormatInfo() {
+ I4 = new TextureFormatInfo(8, 8, 1);
+ I8 = new TextureFormatInfo(8, 4, 2);
+ IA4 = new TextureFormatInfo(8, 4, 2);
+ IA8 = new TextureFormatInfo(4, 4, 4);
+ RGB565 = new TextureFormatInfo(4, 4, 4);
+ RGB5A3 = new TextureFormatInfo(4, 4, 4);
+ RGBA8 = new TextureFormatInfo(4, 4, 8);
+ C4 = new TextureFormatInfo(8, 8, 1);
+ C8 = new TextureFormatInfo(8, 4, 2);
+ C14X2 = new TextureFormatInfo(4, 4, 4);
+ CMPR = new TextureFormatInfo(8, 8, 1);
+ }
+
+ public static TextureFormatInfo GetFormat(TextureFormat format) {
+ switch (format) {
+ case TextureFormat.I4:
+ return I4;
+ case TextureFormat.I8:
+ return I8;
+ case TextureFormat.IA4:
+ return IA4;
+ case TextureFormat.IA8:
+ return IA8;
+ case TextureFormat.RGB565:
+ return RGB565;
+ case TextureFormat.RGB5A3:
+ return RGB5A3;
+ case TextureFormat.RGBA8:
+ return RGBA8;
+ case TextureFormat.C4:
+ return C4;
+ case TextureFormat.C8:
+ return C8;
+ case TextureFormat.C14X2:
+ return C14X2;
+ case TextureFormat.CMPR:
+ return CMPR;
+ default:
+ throw new ArgumentOutOfRangeException("unknown texture format");
+ }
+ }
+
+ public static readonly TextureFormatInfo I4, I8, IA4, IA8, RGB565, RGB5A3, RGBA8, C4, C8, C14X2,
+ CMPR;
+ }
+
+
+
+ public Bitmap BaseImage;
+
+ public TextureFormat Format;
+ public UInt32 MipMapCount;
+ public float MinLOD, MaxLOD;
+
+ public Texture() {
+ }
+
+
+
+
+ public static int GetDataSize(int width, int height, TextureFormat format) {
+ var info = TextureFormatInfo.GetFormat(format);
+
+ // align width, height up
+ width = Misc.AlignUp(width, info.TexelWidth);
+ height = Misc.AlignUp(height, info.TexelHeight);
+
+ return width * height * info.NybblesPerPixel / 2;
+ }
+
+ unsafe public void ImportData(byte[] imgdata, int width, int height, TextureFormat format) {
+ var image = new Bitmap(width, height, PixelFormat.Format32bppArgb);
+ var bits = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, image.PixelFormat);
+
+ var info = TextureFormatInfo.GetFormat(format);
+ int blkWidth = info.TexelWidth;
+ int blkHeight = info.TexelHeight;
+
+ /*int blkNumY = 0;
+
+ for (int blkY = 0; blkY < height; blkY += blkHeight) {
+ byte* pBlkY = (byte*)bits.Scan0 + (blkY * bits.Stride);
+
+ int blkNumX = 0;
+
+ for (int blkX = 0; blkX < width; blkX += blkWidth) {
+ byte* pBlk = pBlkY + (blkX * sizeof(uint));
+
+ for (int y = 0; y < blkHeight; y++) {
+ if ((blkY + y) >= height)
+ break;
+
+ byte* pRow = pBlk + (y * bits.Stride);
+
+ for (int x = 0; x < blkWidth; x++) {
+ if ((blkX + x) >= width)
+ break;
+
+ uint* pPixel = (uint*)pRow + x;
+
+ }
+ }
+
+ blkNumX++;
+ }
+
+ blkNumY++;
+ }*/
+
+ // What I'm missing overall: IA4, IA8, RGBA8, all the palette ones
+
+ // this thing probably breaks on textures which aren't multiples of the texel width/height, too
+ // oh well
+
+ var data = new InputStream(imgdata);
+
+ switch (format) {
+ case TextureFormat.RGB565:
+ case TextureFormat.RGB5A3:
+ case TextureFormat.I4:
+ case TextureFormat.I8:
+ // I4 is stupid and breaks my parser. I'll keep some state here
+ bool alreadyHaveI4Nybble = false;
+ byte currentI4Byte = 0;
+
+ for (int y = 0; y < height; y += blkHeight) {
+ int yMax = y + blkHeight;
+ if (yMax > height)
+ yMax = height;
+
+ for (int x = 0; x < width; x += blkWidth) {
+ int xMax = x + blkWidth;
+ if (xMax > width)
+ xMax = width;
+
+ for (int y1 = y; y1 < yMax; y1++) {
+ byte *pRow = (byte*)bits.Scan0 + (y1 * bits.Stride);
+
+ for (int x1 = x; x1 < xMax; x1++) {
+ uint *pPixel = (uint*)pRow + x1;
+
+ if (format == TextureFormat.RGB565) {
+ ushort val = data.ReadUInt16();
+ uint r = (uint)(val & 0xF800) >> 8;
+ uint g = (uint)(val & 0x7e0) >> 3;
+ uint b = (uint)(val & 0x1f) << 3;
+ *pPixel = 0xFF000000 | (r << 16) | (g << 8) | b;
+
+ } else if (format == TextureFormat.RGB5A3) {
+ ushort val = data.ReadUInt16();
+ if ((val & 0x8000) == 0) {
+ uint a = (uint)(val & 0x7000) >> 3;
+ uint r = (uint)(val & 0xF00) >> 4;
+ uint g = (uint)(val & 0xF0);
+ uint b = (uint)(val & 0xF) << 4;
+ *pPixel = (a << 24) | (r << 16) | (g << 8) | b;
+ } else {
+ uint r = (uint)(val & 0x7c00) >> 7;
+ uint g = (uint)(val & 0x3e0) >> 2;
+ uint b = (uint)(val & 0x1f) << 3;
+ *pPixel = 0xFF000000 | (r << 16) | (g << 8) | b;
+ }
+
+ } else if (format == TextureFormat.I4) {
+ if (!alreadyHaveI4Nybble) {
+ currentI4Byte = data.ReadByte();
+ uint val = (uint)currentI4Byte & 0xF0;
+ *pPixel = 0xFF000000 | (val << 16) | (val << 8) | val;
+ } else {
+ uint val = (uint)(currentI4Byte & 0xF) << 4;
+ *pPixel = 0xFF000000 | (val << 16) | (val << 8) | val;
+ }
+
+ alreadyHaveI4Nybble = !alreadyHaveI4Nybble;
+
+ } else if (format == TextureFormat.I8) {
+ byte val = data.ReadByte();
+ *pPixel = 0xFF000000 | (uint)(val << 16) | (uint)(val << 8) | val;
+ }
+ }
+ }
+ }
+ }
+
+ break;
+
+ case TextureFormat.CMPR:
+ // this code is messy
+
+ ushort[] rawClrArray = new ushort[4];
+ uint[] clrArray = new uint[4];
+
+ for (int y = 0; y < height; y += 8) {
+ for (int x = 0; x < width; x += 8) {
+ for (int iBlockY = 0; iBlockY < 2; iBlockY++) {
+ for (int iBlockX = 0; iBlockX < 2; iBlockX++) {
+ // decode a block
+ rawClrArray[0] = data.ReadUInt16();
+ rawClrArray[1] = data.ReadUInt16();
+
+ bool hasAlpha = false;
+ if (rawClrArray[0] > rawClrArray[1]) {
+ rawClrArray[2] = CMPRAvgColor(2, 1, rawClrArray[0], rawClrArray[1]);
+ rawClrArray[3] = CMPRAvgColor(1, 2, rawClrArray[0], rawClrArray[1]);
+ } else {
+ rawClrArray[2] = CMPRAvgColor(1, 1, rawClrArray[0], rawClrArray[1]);
+ rawClrArray[3] = rawClrArray[1];
+ hasAlpha = true;
+ }
+
+ for (int i = 0; i < 4; i++) {
+ byte r = (byte)(((rawClrArray[i] >> 11) & 0x1F) << 3);
+ byte g = (byte)(((rawClrArray[i] >> 5) & 0x3F) << 2);
+ byte b = (byte)((rawClrArray[i] & 0x1F) << 3);
+ byte a = (byte)(((i == 3) && hasAlpha) ? 0 : 255);
+
+ clrArray[i] = (uint)(a << 24) | (uint)(r << 16) | (uint)(g << 8) | b;
+ }
+
+ for (int inY = 0; inY < 4; inY++) {
+ byte* pRow = (byte*)bits.Scan0 + ((y + (iBlockY * 4) + inY) * bits.Stride);
+ byte val = data.ReadByte();
+
+ for (int inX = 0; inX < 4; inX++) {
+ uint *pPixel = (uint*)pRow + x + (iBlockX * 4) + inX;
+ *pPixel = clrArray[(val >> 6) & 3];
+ val <<= 2;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ break;
+
+ default:
+ Console.WriteLine("Unsupported texture format: {0}", format);
+ break;
+ }
+
+
+ image.UnlockBits(bits);
+
+ BaseImage = image;
+ Format = format;
+ }
+
+ private static ushort CMPRAvgColor(ushort w0, ushort w1, ushort c0, ushort c1) {
+ uint result;
+
+ ushort a0 = (ushort)(c0 >> 11);
+ ushort a1 = (ushort)(c1 >> 11);
+ uint a = (uint)((w0*a0 + w1*a1) / (w0+w1));
+ result = (a << 11) & 0xffff;
+
+ a0 = (ushort)((c0 >> 5) & 63);
+ a1 = (ushort)((c1 >> 5) & 63);
+ a = (uint)((w0 * a0 + w1 * a1) / (w0 + w1));
+ result |= ((a << 5) & 0xffff);
+
+ a0 = (ushort)(c0 & 31);
+ a1 = (ushort)(c1 & 31);
+ a = (uint)((w0 * a0 + w1 * a1) / (w0 + w1));
+ result |= a;
+
+ return (ushort)result;
+ }
+ }
+}
+
diff --git a/NW4RTools/bin/Debug/NW4RTools.dll b/NW4RTools/bin/Debug/NW4RTools.dll
index 611af6c..870660f 100755
--- a/NW4RTools/bin/Debug/NW4RTools.dll
+++ b/NW4RTools/bin/Debug/NW4RTools.dll
Binary files differ
diff --git a/NW4RTools/bin/Debug/NW4RTools.dll.mdb b/NW4RTools/bin/Debug/NW4RTools.dll.mdb
index a9dff6f..d8889f1 100644
--- a/NW4RTools/bin/Debug/NW4RTools.dll.mdb
+++ b/NW4RTools/bin/Debug/NW4RTools.dll.mdb
Binary files differ
diff --git a/TestApp/Main.cs b/TestApp/Main.cs
index afe6dda..408c95f 100644
--- a/TestApp/Main.cs
+++ b/TestApp/Main.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Collections.Generic;
using NW4RTools;
namespace TestApp {
@@ -7,7 +8,7 @@ namespace TestApp {
public static void Main(string[] args) {
string mdlPath = "/home/me/Games/Newer/ModelRev/";
- string mdlName = "CS_W7";
+ string mdlName = "CS_W9";
//string mdlName = "bgB_4502";
string whatever = (mdlName == "CS_W2" || mdlName == "CS_W3" || mdlName == "CS_W6") ? "a" : "";
@@ -15,6 +16,12 @@ namespace TestApp {
byte[] file = File.ReadAllBytes(mdlPath + mdlName + ".brres");
ResFile rf = BrresReader.LoadFile(file);
+ var texs = rf.GetGroup<Texture>("Textures(NW4R)");
+ // wtf C#?!
+ foreach (var kv in (IEnumerable<KeyValuePair<string,Texture>>)texs) {
+ kv.Value.BaseImage.Save(mdlPath + kv.Key + ".png");
+ }
+
//var objFile = File.Open(mdlPath + mdlName + ".obj", FileMode.OpenOrCreate);
var objFile = File.Open(mdlPath + mdlName + ".dae", FileMode.OpenOrCreate);
//var sw = new StreamWriter(objFile);
diff --git a/TestApp/bin/Debug/NW4RTools.dll b/TestApp/bin/Debug/NW4RTools.dll
index 611af6c..870660f 100755
--- a/TestApp/bin/Debug/NW4RTools.dll
+++ b/TestApp/bin/Debug/NW4RTools.dll
Binary files differ
diff --git a/TestApp/bin/Debug/NW4RTools.dll.mdb b/TestApp/bin/Debug/NW4RTools.dll.mdb
index a9dff6f..d8889f1 100644
--- a/TestApp/bin/Debug/NW4RTools.dll.mdb
+++ b/TestApp/bin/Debug/NW4RTools.dll.mdb
Binary files differ
diff --git a/TestApp/bin/Debug/TestApp.exe b/TestApp/bin/Debug/TestApp.exe
index 5845276..4c3f608 100755
--- a/TestApp/bin/Debug/TestApp.exe
+++ b/TestApp/bin/Debug/TestApp.exe
Binary files differ
diff --git a/TestApp/bin/Debug/TestApp.exe.mdb b/TestApp/bin/Debug/TestApp.exe.mdb
index a86d949..46a6888 100644
--- a/TestApp/bin/Debug/TestApp.exe.mdb
+++ b/TestApp/bin/Debug/TestApp.exe.mdb
Binary files differ