BigheadSMZ

Cold Steel I / II Phyre Texture Format

Jan 26th, 2019
291
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ================================================================================================================================
  2. THE PHYRE IMAGE HEADER (WIP)
  3. ================================================================================================================================
  4. This is my attempt at explaining everything that I figured out about the phyre image format by studying many Cold Steel I & II images in a hex editor and understanding important values and flags. The reason was to be able to automate a process that can rewrite the headers to hold new data for an image with the ability to supply entirely new pixel data to basically create a new image. While I was not able to gain a clear enough understanding to write a header from scratch, it is possible to update a header with brand new image data that contains a new image format, width, height, and even a different number of mipmaps than the original texture. The header can be updated to a point where a PNG image can replace a DDS image and vice versa, the format can be altered and the phyre engine won't care that the image has changed (except in a few rare cases where the game seems to expect a certain format).
  5.  
  6. NOTICE: If nothing here seems to make any sense, open a few different phyre images in a hex editor and try to follow along!
  7. ================================================================================================================================
  8. STATIC HEADER DATA:
  9. ================================================================================================================================
  10. This data can always be found at the exact locations specified.
  11. ================================================================================================================================
  12. 0x034 - This is some flag that seems to be exclusive to phyre headers. It is tied to the image format, and is consistent for the image formats below:
  13.  
  14. L8/A8 : 14 (0E hex)
  15. LA8 : 15 (0F hex)
  16. DXT1/3/5 : 16 (10 hex)
  17. RGBA/ARGB : 17 (11 hex)
  18.  
  19. 0x038 - The value here is equal to the length of data that starts at the image path (0x99B), and extends until:
  20.  
  21. ARGB/RGBA : the byte after the first known value of 88 (58 hex). I don't know why an extra byte is calculated in this case.
  22. DXT1/3/5 : the second known value of 88 (58 hex) is found. The first value of 88 (58 hex) represents the X in "DXT*" so it is skipped.
  23.  
  24. 0x050 - The texture pitch, which is 4 bytes long (extends to 0x53). Unlike standard DDS formats, the computation is the same for compressed and uncompressed images. The difference is that uncompressed textures use the compressed formula (as defined by Microsoft), and are padded to 64 bytes per block, although they don't technically use compression blocks. Although not technically correct either, can be looked at as "bytes per pixel", except for DXT1 images where it is technically more correct to say that each pixel is 0.5 bytes, but not exactly correct since size depends on blocks. I guess all you can do is trust that the values below are correct. (I have personally confirmed all are correct except "A8", but it makes sense).
  25.  
  26. Block Size List:
  27. DXT1 : 8
  28. DXT3 : 16
  29. DXT5 : 16
  30. ARGB8 : 64
  31. RGBA8 : 64
  32. L8 : 16
  33. A8 : 16
  34. LA8 : 32
  35.  
  36. To calculate the pitch, use the formula below:
  37. Pitch = Max(1,(Width / 4)) * Max(1,(Height / 4)) * BlockSize
  38.  
  39. 0x957 - Always 112 (70 hex) for CS1 textures, 116 (74 hex) for CS2 textures. I imagine this is the length of data from (some location) to (some location).
  40.  
  41. 0x95B - Always 112 (70 hex) for CS1 textures, 116 (74 hex) for CS2 textures. I imagine this is the length of data from (some location) to (some location).
  42.  
  43. * As can be seen, the above two bytes are always identical for all phyre images in CS1 and CS2. I have yet to delve into other games using the engine.
  44.  
  45. 0x99B - The relative image path. This will always be padded/aligned by up to 4 bytes, and the length determines the offset of the rest of the bytes.
  46.  
  47. ================================================================================================================================
  48. DYNAMIC HEADER DATA:
  49. ================================================================================================================================
  50. Because the image path can be of variable length, the rest of the data is at an arbitrary position. The easiest way I found is to find where the phrase "PTexture2D" starts (hex sequence: 50 54 65 78 74 75 72 65 32 44), so in this case, the byte that contains 50 in hex (80 decimal) is the "Start Location". From here, the rest of the data can be found at static locations by subtracting a fixed amount from the start location.
  51. ================================================================================================================================
  52. So now that you may have a basic idea of how this works, allow me to further complicate it. It's been established the length of the path is variable, but so is the length of the texture format. This means that the starting point now also needs to account for the length of the path to find data beyond this point. To make this much easier, I'll try to create a scheme that is easy to understand where this data resides.
  53. ================================================================================================================================
  54. Start location will be referred to as "$".
  55. Texture format length added to start location will be referred to as "#".
  56. Using "+" and "-" will refer in which direction to go from the start location.
  57. Example: $-0x004 would mean if the starting byte was "0xA2B" (2603), the data's position can be found at "0xA27" (2599).
  58. Example: $#+0x004 would mean if the starting byte was "0xA2B", and if format is "ARGB8" (5 digits = 5 bytes), the data's position can be found at "0xA34".
  59. To further explain the last example, it can be seen as: (start location + 5 bytes format length + 4 offset bytes).
  60. ================================================================================================================================
  61. COLD STEEL I: All values can be found by subtracting from the start location.
  62. ================================================================================================================================
  63. $-0x064 - The literal number of mipmaps if its a DDS texture; requires the number of mipmaps in both this field and the one below. Always "00" for PNG.
  64.  
  65. $-0x060 - The literal number of mipmaps for both PNG and DDS. Unlike standard DDS images, only lower mipmaps are counted and not the base level.
  66.  
  67. $-0x05C - Special flag for PNG textures which is always the value "02". For DDS images, this is always "00".
  68.  
  69. $-0x054 - The width of the texture, four bytes in length. Little endian.
  70.  
  71. $-0x050 - The height of the texture, four bytes in length. Little endian.
  72. ================================================================================================================================
  73. COLD STEEL II: All values are the same as CS1, except there is an additional 4 byte offset.
  74. ================================================================================================================================
  75. $-0x068 - The literal number of mipmaps if its a DDS texture; requires the number of mipmaps in both this field and the one below. Always "00" for PNG.
  76.  
  77. $-0x064 - The literal number of mipmaps for both PNG and DDS. Unlike standard DDS images, only lower mipmaps are counted and not the base level.
  78.  
  79. $-0x060 - Special flag for PNG textures which is always the value "02". For DDS images, this is always "00".
  80.  
  81. $-0x058 - The width of the texture, four bytes in length. Little endian.
  82.  
  83. $-0x054 - The height of the texture, four bytes in length. Little endian.
  84. ================================================================================================================================
  85. COLD STEEL I & II
  86. ================================================================================================================================
  87. $+0x011 - The texture format, following "PTexture2D.". Each digit of a format is represented with 1 byte, so the length in bytes is equal to the number of digits. Further values require adding this value to the offset to find the rest of the data. The format will always be followed by "00" in the next byte, so this could also be useful for coming up with a way to determine the offsets of data.
  88.  
  89. Image can be any of the following formats:
  90. DXT1 - # = 4 bytes - (44 58 54 31)
  91. DXT3 - # = 4 bytes - (44 58 54 33)
  92. DXT5 - # = 4 bytes - (44 58 54 35)
  93. ARGB8 - # = 5 bytes - (41 52 47 42 38)
  94. RGBA8 - # = 5 bytes - (52 47 42 41 38)
  95. L8 - # = 2 bytes - (4C 38)
  96. A8 - # = 2 bytes - (41 38)
  97. LA8 - # = 3 bytes - (4C 41 38)
  98.  
  99. $#+0x01D - This flag should be located 18 bytes after the last byte of where the texture format ends. Like the flag found at 0x034, it is exclusive to phyre headers, and it is tied to the image format.
  100.  
  101. It is consistent for the image formats below:
  102. L8/A8 : 03
  103. LA8 : 04
  104. DXT1/3/5 : 05
  105. RGBA/ARGB : 06
  106. ================================================================================================================================
RAW Paste Data