You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
3.2 KiB

4 years ago
  1. """
  2. A Pillow loader for .ftc and .ftu files (FTEX)
  3. Jerome Leclanche <jerome@leclan.ch>
  4. The contents of this file are hereby released in the public domain (CC0)
  5. Full text of the CC0 license:
  6. https://creativecommons.org/publicdomain/zero/1.0/
  7. Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001
  8. The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a
  9. packed custom format called FTEX. This file format uses file extensions FTC
  10. and FTU.
  11. * FTC files are compressed textures (using standard texture compression).
  12. * FTU files are not compressed.
  13. Texture File Format
  14. The FTC and FTU texture files both use the same format. This
  15. has the following structure:
  16. {header}
  17. {format_directory}
  18. {data}
  19. Where:
  20. {header} = { u32:magic, u32:version, u32:width, u32:height, u32:mipmap_count, u32:format_count }
  21. * The "magic" number is "FTEX".
  22. * "width" and "height" are the dimensions of the texture.
  23. * "mipmap_count" is the number of mipmaps in the texture.
  24. * "format_count" is the number of texture formats (different versions of the
  25. same texture) in this file.
  26. {format_directory} = format_count * { u32:format, u32:where }
  27. The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB
  28. uncompressed textures.
  29. The texture data for a format starts at the position "where" in the file.
  30. Each set of texture data in the file has the following structure:
  31. {data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } }
  32. * "mipmap_size" is the number of bytes in that mip level. For compressed
  33. textures this is the size of the texture data compressed with DXT1. For 24 bit
  34. uncompressed textures, this is 3 * width * height. Following this are the image
  35. bytes for that mipmap level.
  36. Note: All data is stored in little-Endian (Intel) byte order.
  37. """
  38. import struct
  39. from io import BytesIO
  40. from . import Image, ImageFile
  41. MAGIC = b"FTEX"
  42. FORMAT_DXT1 = 0
  43. FORMAT_UNCOMPRESSED = 1
  44. class FtexImageFile(ImageFile.ImageFile):
  45. format = "FTEX"
  46. format_description = "Texture File Format (IW2:EOC)"
  47. def _open(self):
  48. magic = struct.unpack("<I", self.fp.read(4))
  49. version = struct.unpack("<i", self.fp.read(4))
  50. self._size = struct.unpack("<2i", self.fp.read(8))
  51. mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))
  52. self.mode = "RGB"
  53. # Only support single-format files.
  54. # I don't know of any multi-format file.
  55. assert format_count == 1
  56. format, where = struct.unpack("<2i", self.fp.read(8))
  57. self.fp.seek(where)
  58. mipmap_size, = struct.unpack("<i", self.fp.read(4))
  59. data = self.fp.read(mipmap_size)
  60. if format == FORMAT_DXT1:
  61. self.mode = "RGBA"
  62. self.tile = [("bcn", (0, 0) + self.size, 0, (1))]
  63. elif format == FORMAT_UNCOMPRESSED:
  64. self.tile = [("raw", (0, 0) + self.size, 0, ('RGB', 0, 1))]
  65. else:
  66. raise ValueError(
  67. "Invalid texture compression format: %r" % (format))
  68. self.fp.close()
  69. self.fp = BytesIO(data)
  70. def load_seek(self, pos):
  71. pass
  72. def _validate(prefix):
  73. return prefix[:4] == MAGIC
  74. Image.register_open(FtexImageFile.format, FtexImageFile, _validate)
  75. Image.register_extensions(FtexImageFile.format, [".ftc", ".ftu"])