klensy/wt-tools

Can't open ddsx files from Cuisine Royale

Twisted6 opened this issue · 6 comments

Hey klensy, it's me again xD

I'm trying to open the DDSX map images using ddsx_unpack.exe but I can't get it to work. I've tried placing both oo2core_6_win64.dll and the latest oo2core_8_win64.dll in the same folder as ddsx_unpack.exe. The console pops up for a second and then closes. Am I doing something wrong or are these particular files just not supported? I've attached one of the ddsx files.
mexico_4x4_minimap.zip

Hi.

  1. You should run tools from command line or powershell, if you want error info.
  2. Fixed, will upload soon.

Need auto upload builds to releases, it's annoying to upload by hands.

Uploaded, check it.

But it looks little broken.

Thanks again for the quick response. It's almost working perfectly but on one of the maps the bottom half is on the top and has been scaled weirdly. I've included examples of a working map and a cut map.
https://www.dropbox.com/s/ay7hqqdpbmca02l/Screenshot%202020-05-05%2013.55.18.png?dl=0
https://www.dropbox.com/s/87mkqy1rymah11j/Mexico.jpg?dl=0

Lots of the DDSX files I've tried from Cuisine Royale had the FLG_REV_MIP_ORDER flag set. That means the mipmaps appear in reverse order, from the smallest mipmap to the biggest. In DDS files, this must be the opposite order (from the biggest mipmap to the smallest). Thus, the mipmaps order must be reversed before writing the content to the DDS file.

I did the following changes to ddsx_unpack.py to reverse the mipmaps order and it works for all the DDSX files I've tried for CRSED:

Reversed mipmap patch for DXT1
--- wt-tools/ddsx_unpack-original.py    2020-05-28 08:44:30.000000000 +0200
+++ wt-tools/ddsx_unpack.py     2021-01-11 04:57:59.646787500 +0100
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+
 import struct, sys, ctypes, zlib
 import os.path
 import pylzma
@@ -61,11 +63,11 @@

     dds_packed = compression_type.get(dds_compression_type, "")
     if dds_packed == "not_packed":
-        return dds_data.raw + data[0x20:]
+        d_data = data[0x20:]
     elif dds_packed == "lzma":
-        return dds_data.raw + pylzma.decompress(data[0x20:], maxlength=parsed_data.header.memSz)
+        d_data = pylzma.decompress(data[0x20:], maxlength=parsed_data.header.memSz)
     elif dds_packed == "zlib":
-        return dds_data.raw + zlib.decompress(data[0x20:])
+        d_data = zlib.decompress(data[0x20:])
     elif dds_packed == "oodle":
         '''
         private static extern long OodleLZ_Decompress(byte[] buffer, long bufferSize, byte[] result,
@@ -79,7 +81,7 @@
             if res == 0:
                 print("Error unpacking oodle compressed texture")
                 return
-            return dds_data.raw + decompressed_data.raw
+            d_data = decompressed_data.raw
         else:
             print("unsupported compression type: {}".format(dds_packed))
     elif dds_packed == "zstd":
@@ -88,11 +90,44 @@
         if d_data == 0:
             print("Error unpacking zstd compressed texture")
             return
-        return dds_data.raw + d_data
     else:
         print("Unknown compression type: {}".format(dds_compression_type))
         return

+    if parsed_data.header.flags.FLG_REV_MIP_ORDER:
+        # Reverse MIPMAP order (from smallest -> biggest to biggest -> smallest)
+        if texture_format == b'DXT1':
+            def get_dxt1_size(width, height):
+                # Each 4x4 blocks use 8 bytes
+                # XXX We suppose width and height must be rounded up so that they are multiple of 4
+                if (width & 3) != 0:
+                    width += 4 - (width & 3)
+                if (height & 3) != 0:
+                    height += 4 - (height & 3)
+
+                blocks = (width * height) // (4 * 4)
+                return blocks * 8
+
+            pos = 0
+            images = []
+            for level in range(parsed_data.header.levels - 1, -1, -1):
+                width = parsed_data.header.w // (2**level)
+                height = parsed_data.header.h // (2**level)
+                size = get_dxt1_size(width, height)
+                # print("%d => %d x %d (%d)" % (level, width, height, size))
+                images.append(d_data[pos:pos+size])
+                pos += size
+
+            d_data = bytearray()
+            for image in reversed(images):
+                d_data.extend(image)
+
+        elif parsed_data.header.levels > 1:
+            print("Dunno how to re-order mipmaps for format {}".format(texture_format))
+            return
+
+    return dds_data.raw + d_data
+

 def unpack_file(filename):
     # TODO: eliminate copy&paste with blk_unpack

@jdoe1024 Thanks for patch! But better send it as attached file or pull request.

build https://github.com/klensy/wt-tools/actions/runs/510036724