Skip to content

Commit

Permalink
gust_g1t: add support for Z-Stacked textures
Browse files Browse the repository at this point in the history
Some games (e.g. Blue Reflection: Second Light) use stacked DDS textures where the actual
texture height is a multiple of the formal reported height.
This enables the application to switch between 2 or more textures from within the same DDS
according to a Z index.
Also improve four CC and normal flag handling.

Closes #50
  • Loading branch information
VitaSmith committed Nov 13, 2021
1 parent 6f31674 commit 74192fb
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .vs/gust_g1t.vcxproj.user
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerWorkingDirectory>$(SolutionDir)</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerCommandArguments>H_MIU_A.g1t</LocalDebuggerCommandArguments>
<LocalDebuggerCommandArguments>type_5b_win_3.g1t</LocalDebuggerCommandArguments>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerWorkingDirectory>$(SolutionDir)</LocalDebuggerWorkingDirectory>
Expand Down
49 changes: 45 additions & 4 deletions dds.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
DDS definitions
Copyright © 2019-2020 VitaSmith
Copyright © 2019-2021 VitaSmith
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand All @@ -22,13 +22,21 @@

#define DDS_MAGIC 0x20534444 // "DDS "

#define DDS_ALPHAPIXELS 0x00000001 // DDPF_ALPHAPIXELS
#define DDS_ALPHA 0x00000002 // DDPF_ALPHA
#define DDS_FOURCC 0x00000004 // DDPF_FOURCC
#define DDS_PAL4 0x00000008 // DDPF_PALETTEINDEXED4
#define DDS_PAL8 0x00000020 // DDPF_PALETTEINDEXED8
#define DDS_RGB 0x00000040 // DDPF_RGB
#define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS
#define DDS_PAL1 0x00000800 // DDPF_PALETTEINDEXED1
#define DDS_PAL2 0x00001000 // DDPF_PALETTEINDEXED2
#define DDS_PREMULTALPHA 0x00008000 // DDPF_ALPHAPREMULT
#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE
#define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS
#define DDS_ALPHA 0x00000002 // DDPF_ALPHA
#define DDS_PAL8 0x00000020 // DDPF_PALETTEINDEXED8
// Custom NVTT flags.
#define DDS_SRGB 0x40000000; // DDPF_SRGB
#define DDS_NORMAL 0x80000000; // DDPF_NORMAL

#define DDS_HEADER_FLAGS_TEXTURE 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
#define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT
Expand Down Expand Up @@ -65,15 +73,26 @@ enum DDS_FORMAT {
DDS_FORMAT_ARGB,
DDS_FORMAT_GRAB,
DDS_FORMAT_RGBA,
DDS_FORMAT_RXGB,
DDS_FORMAT_R,
DDS_FORMAT_UVER,
DDS_FORMAT_DXT1,
DDS_FORMAT_DXT2,
DDS_FORMAT_DXT3,
DDS_FORMAT_DXT4,
DDS_FORMAT_DXT5,
DDS_FORMAT_DX10,
DDS_FORMAT_BC4,
DDS_FORMAT_BC5,
DDS_FORMAT_BC6,
DDS_FORMAT_BC7
DDS_FORMAT_BC7,
DDS_FORMAT_BC6H,
DDS_FORMAT_BC7L,
DDS_FORMAT_ATI1,
DDS_FORMAT_ATI2,
DDS_FORMAT_A2XY,
DDS_FORMAT_DDS,
DDS_FORMAT_NVTT,
};

enum DXGI_FORMAT {
Expand Down Expand Up @@ -267,16 +286,38 @@ static __inline uint32_t get_fourCC(int format)
switch (format) {
case DDS_FORMAT_DXT1:
return MAKEFOURCC('D', 'X', 'T', '1');
case DDS_FORMAT_DXT2:
return MAKEFOURCC('D', 'X', 'T', '2');
case DDS_FORMAT_DXT3:
return MAKEFOURCC('D', 'X', 'T', '3');
case DDS_FORMAT_DXT4:
return MAKEFOURCC('D', 'X', 'T', '4');
case DDS_FORMAT_DXT5:
return MAKEFOURCC('D', 'X', 'T', '5');
case DDS_FORMAT_ATI1:
case DDS_FORMAT_BC4:
return MAKEFOURCC('A', 'T', 'I', '1');
case DDS_FORMAT_ATI2:
return MAKEFOURCC('A', 'T', 'I', '2');
case DDS_FORMAT_A2XY:
return MAKEFOURCC('A', '2', 'X', 'Y');
case DDS_FORMAT_BC7:
case DDS_FORMAT_DX10:
return MAKEFOURCC('D', 'X', '1', '0');
case DDS_FORMAT_BC7L:
return MAKEFOURCC('B', 'C', '7', 'L');
case DDS_FORMAT_NVTT:
return MAKEFOURCC('N', 'V', 'T', 'T');
case DDS_FORMAT_DDS:
return MAKEFOURCC('D', 'D', 'S', ' ');
case DDS_FORMAT_RXGB:
return MAKEFOURCC('R', 'X', 'G', 'B');
case DDS_FORMAT_UVER:
return MAKEFOURCC('U', 'V', 'E', 'R');
case DDS_FORMAT_BC6H:
return MAKEFOURCC('B', 'C', '6', 'H');
default:
fprintf(stderr, "WARNING: Unsupported fourCC");
return 0;
}
}
104 changes: 66 additions & 38 deletions gust_g1t.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>

Expand All @@ -35,7 +34,7 @@
// G1T texture flags
#define G1T_FLAG_SRGB 0x0002000000ULL // Not sure if this one is correct...
#define G1T_FLAG_LOCAL_XDATA 0x0010000000ULL // Set if the texture has local extra data in the texture entry.
#define G1T_FLAG_GLOBAL_XDATA 0x1000000000ULL // Set if the texture has global extra data at the beginning of the file.
#define G1T_FLAG_IS_NORMAL_MAP 0x1000000000ULL // Set if the texture is a normal map.

// Known platforms
#define SONY_PS2 0x00
Expand Down Expand Up @@ -154,6 +153,8 @@ static size_t write_dds_header(FILE* fd, int format, uint32_t width, uint32_t he
header.flags |= DDS_HEADER_FLAGS_MIPMAP;
header.caps |= DDS_SURFACE_FLAGS_MIPMAP;
}
if (flags & G1T_FLAG_IS_NORMAL_MAP)
header.ddspf.flags |= DDS_NORMAL;
size_t r = fwrite(&header, sizeof(DDS_HEADER), 1, fd);
if (r != 1)
return r;
Expand Down Expand Up @@ -430,14 +431,17 @@ int main_utf8(int argc, char** argv)
dir[get_trailing_slash(dir)] = 0;
for (size_t i = 0; i < strlen(_basename(argv[argc - 1])); i++)
putchar(' ');
printf(" DIMENSIONS MIPMAPS SUPPORTED?\n");
printf(" DIMENSIONS MIPMAPS ZSTACK\n");
for (uint32_t i = 0; i < getv32(hdr.nb_textures); i++) {
offset_table[i] = ftell(file) - getv32(hdr.header_size);
JSON_Object* texture_entry = json_array_get_object(textures_array, i);
g1t_tex_header tex = { 0 };
tex.type = json_object_get_uint8(texture_entry, "type");
tex.exts = json_object_get_uint8(texture_entry, "exts");
uint64_t flags = json_object_get_uint64(texture_entry, "flags");
uint32_t z_stack = json_object_get_uint32(texture_entry, "z_stack");
if (z_stack == 0)
z_stack = 1;
tex.hflags = (uint8_t)(flags >> 32);
tex.lflags = (uint32_t)flags;
// Read the DDS file
Expand All @@ -457,6 +461,8 @@ int main_utf8(int argc, char** argv)
DDS_HEADER* dds_header = (DDS_HEADER*)&buf[sizeof(uint32_t)];
dds_size -= sizeof(uint32_t) + sizeof(DDS_HEADER);
uint8_t* dds_payload = (uint8_t*)&buf[sizeof(uint32_t) + sizeof(DDS_HEADER)];
// Adjust the height if we have a Z Stack
dds_header->height /= z_stack;
// We may have a DXT10 additional header
if (dds_header->ddspf.fourCC == get_fourCC(DDS_FORMAT_DX10)) {
dds_size -= sizeof(DDS_HEADER_DXT10);
Expand Down Expand Up @@ -532,34 +538,39 @@ int main_utf8(int argc, char** argv)
break;
}
uint32_t bpp = 0, texture_format = default_texture_format;
bool supported = true, swizzled = false;
bool swizzled = false;
switch (tex.type) {
case 0x00: bpp = 32; break;
case 0x01: bpp = 32; break;
case 0x02: bpp = 32; break;
case 0x03: bpp = 64; supported = false; break; // UNSUPPORTED!!
case 0x04: bpp = 128; supported = false; break; // UNSUPPORTED!!
// case 0x03: bpp = 64; break;
// case 0x04: bpp = 128; break;
case 0x06: texture_format = DDS_FORMAT_DXT1; bpp = 4; break;
case 0x07: texture_format = DDS_FORMAT_DXT3; bpp = 8; supported = false; break; // UNSUPPORTED!!
// case 0x07: texture_format = DDS_FORMAT_DXT3; bpp = 8; break;
case 0x08: texture_format = DDS_FORMAT_DXT5; bpp = 8; break;
case 0x09: bpp = 32; swizzled = true; break;
case 0x0A: bpp = 32; swizzled = true; supported = false; break; // UNSUPPORTED!!
// case 0x0A: bpp = 32; swizzled = true; break;
case 0x10: texture_format = DDS_FORMAT_DXT1; bpp = 4; swizzled = true; break;
case 0x12: texture_format = DDS_FORMAT_DXT5; bpp = 8; swizzled = true; break;
case 0x21: bpp = 32; break;
case 0x3C: texture_format = DDS_FORMAT_DXT1; bpp = 16; supported = false; break; // UNSUPPORTED!!
case 0x3D: texture_format = DDS_FORMAT_DXT1; bpp = 16; supported = false; break; // UNSUPPORTED!!
case 0x3C: texture_format = DDS_FORMAT_DXT1; bpp = 16; break;
case 0x3D: texture_format = DDS_FORMAT_DXT1; bpp = 16; break;
case 0x45: texture_format = DDS_FORMAT_BGR; bpp = 24; swizzled = true; break;
case 0x59: texture_format = DDS_FORMAT_DXT1; bpp = 4; break;
case 0x5B: texture_format = DDS_FORMAT_DXT5; bpp = 8; break;
case 0x5C: texture_format = DDS_FORMAT_BC4; bpp = 4; break;
// case 0x5D: texture_format = DDS_FORMAT_BC5; bits_per_pixel = ?; break;
// case 0x5E: texture_format = DDS_FORMAT_BC6; bits_per_pixel = ?; break;
// case 0x5D: texture_format = DDS_FORMAT_ATI1; bpp = ?; break;
// case 0x5E: texture_format = DDS_FORMAT_ATI2; bpp = ?; break;
case 0x5F: texture_format = DDS_FORMAT_BC7; bpp = 8; break;
case 0x60: texture_format = DDS_FORMAT_DXT1; bpp = 4; swizzled = true; break;
case 0x62: texture_format = DDS_FORMAT_DXT5; bpp = 8; swizzled = true; break;
// case 0x63: texture_format = DDS_FORMAT_BC4; bpp = 4; swizzled = true; break;
// case 0x64: texture_format = DDS_FORMAT_BC5; bpp = 8; swizzled = true; break;
// case 0x65: texture_format = DDS_FORMAT_BC6; bpp = 4, swizzled = true; break;
// case 0x66: texture_format = DDS_FORMAT_BC7; bpp = 8; swizzled = true; break;
// case 0x72: texture_format = DDS_FORMAT_????; bpp = ?; break;
default:
fprintf(stderr, "ERROR: Unhandled texture type 0x%02x\n", tex.type);
fprintf(stderr, "ERROR: Unsupported texture type 0x%02x\n", tex.type);
goto out;
}

Expand All @@ -568,7 +579,7 @@ int main_utf8(int argc, char** argv)
goto out;
}

switch (dds_header->ddspf.flags) {
switch (dds_header->ddspf.flags & (DDS_ALPHAPIXELS | DDS_FOURCC | DDS_RGB)) {
case DDS_RGBA:
if ((dds_header->ddspf.RGBBitCount != 32) && (dds_header->ddspf.RGBBitCount != 64) &&
(dds_header->ddspf.RGBBitCount != 128)) {
Expand Down Expand Up @@ -630,9 +641,9 @@ int main_utf8(int argc, char** argv)
}
char dims[16];
snprintf(dims, sizeof(dims), "%dx%d", dds_header->width, dds_header->height);
printf("0x%02x 0x%08x 0x%08x %s %-10s %-7d %s\n", tex.type, getv32(hdr.header_size) + offset_table[i],
printf("0x%02x 0x%08x 0x%08x %s %-10s %-7d %-6d\n", tex.type, getv32(hdr.header_size) + offset_table[i],
(uint32_t)ftell(file) - offset_table[i] - getv32(hdr.header_size) - (uint32_t)sizeof(g1t_tex_header), path,
dims, dds_header->mipMapCount, supported ? "Y" : "N");
dims, dds_header->mipMapCount, z_stack);
free(buf);
buf = NULL;
}
Expand Down Expand Up @@ -729,7 +740,8 @@ int main_utf8(int argc, char** argv)
json_object_set_number(json_object(json), "nb_textures", hdr->nb_textures);
json_object_set_number(json_object(json), "platform", hdr->platform);
json_object_set_number(json_object(json), "extra_size", hdr->extra_size);
json_object_set_boolean(json_object(json), "flip", flip_image);
if (flip_image)
json_object_set_boolean(json_object(json), "flip", true);

g1t_pos[0] = 0;
if (!list_only && !create_path(argv[argc - 1]))
Expand All @@ -746,7 +758,7 @@ int main_utf8(int argc, char** argv)
printf("TYPE OFFSET SIZE NAME");
for (size_t i = 0; i < strlen(_basename(argv[argc - 1])); i++)
putchar(' ');
printf(" DIMENSIONS MIPMAPS SUPPORTED?\n");
printf(" DIMENSIONS MIPMAPS ZSTACK\n");
dir = strdup(argv[argc - 1]);
if (dir == NULL) {
fprintf(stderr, "ERROR: Alloc error\n");
Expand Down Expand Up @@ -775,7 +787,7 @@ int main_utf8(int argc, char** argv)
// There's an array of flags after the hdr
json_array_append_number(json_array(json_extra_flags_array),
getle32(&buf[(uint32_t)sizeof(g1t_header) + 4 * i]));
uint32_t pos = hdr->header_size + getv32(x_offset_table[i]);
uint32_t z_stack = 1, pos = hdr->header_size + getv32(x_offset_table[i]);
g1t_tex_header* tex = (g1t_tex_header*)&buf[pos];
if (data_endianness != platform_endianness) {
uint8_t swap_tmp = tex->dx;
Expand Down Expand Up @@ -809,34 +821,40 @@ int main_utf8(int argc, char** argv)
json_object_set_number(json_object(json_texture), "exts", tex->exts);
uint32_t texture_format = default_texture_format;
uint32_t bpp = 0; // Bits per pixel
bool supported = true, swizzled = false;
bool swizzled = false;
switch (tex->type) {
case 0x00: bpp = 32; break;
case 0x01: bpp = 32; break;
case 0x02: bpp = 32; break;
case 0x03: bpp = 64; supported = false; break; // UNSUPPORTED!!
case 0x04: bpp = 128; supported = false; break; // UNSUPPORTED!!
// case 0x03: bpp = 64; break;
// case 0x04: bpp = 128; break;
case 0x06: texture_format = DDS_FORMAT_DXT1; bpp = 4; break;
case 0x07: texture_format = DDS_FORMAT_DXT3; bpp = 8; supported = false; break; // UNSUPPORTED!!
// case 0x07: texture_format = DDS_FORMAT_DXT3; bpp = 8; break;
case 0x08: texture_format = DDS_FORMAT_DXT5; bpp = 8; break;
case 0x09: bpp = 32; swizzled = true; break;
case 0x0A: bpp = 32; swizzled = true; supported = false; break; // UNSUPPORTED!!
// case 0x0A: bpp = 32; swizzled = true; break;
case 0x10: texture_format = DDS_FORMAT_DXT1; bpp = 4; swizzled = true; break;
case 0x12: texture_format = DDS_FORMAT_DXT5; bpp = 8; swizzled = true; break;
case 0x21: bpp = 32; break;
case 0x3C: texture_format = DDS_FORMAT_DXT1; bpp = 16; supported = false; break; // UNSUPPORTED!!
case 0x3D: texture_format = DDS_FORMAT_DXT1; bpp = 16; supported = false; break; // UNSUPPORTED!!
case 0x3C: texture_format = DDS_FORMAT_DXT1; bpp = 16; break;
case 0x3D: texture_format = DDS_FORMAT_DXT1; bpp = 16; break;
case 0x45: texture_format = DDS_FORMAT_BGR; bpp = 24; swizzled = true; break;
case 0x59: texture_format = DDS_FORMAT_DXT1; bpp = 4; break;
case 0x5B: texture_format = DDS_FORMAT_DXT5; bpp = 8; break;
case 0x5C: texture_format = DDS_FORMAT_BC4; bpp = 4; break;
// case 0x5D: texture_format = DDS_FORMAT_BC5; bits_per_pixel = ?; break;
// case 0x5E: texture_format = DDS_FORMAT_BC6; bits_per_pixel = ?; break;
// case 0x5D: texture_format = DDS_FORMAT_ATI1; bpp = ?; break;
// case 0x5E: texture_format = DDS_FORMAT_ATI2; bpp = ?; break;
case 0x5F: texture_format = DDS_FORMAT_BC7; bpp = 8; break;
case 0x60: texture_format = DDS_FORMAT_DXT1; bpp = 4; swizzled = true; break;
case 0x62: texture_format = DDS_FORMAT_DXT5; bpp = 8; swizzled = true; break;
// case 0x63: texture_format = DDS_FORMAT_BC4; bpp = 4; swizzled = true; break;
// case 0x64: texture_format = DDS_FORMAT_BC5; bpp = 8; swizzled = true; break;
// case 0x65: texture_format = DDS_FORMAT_BC6; bpp = 4, swizzled = true; break;
// case 0x66: texture_format = DDS_FORMAT_BC7; bpp = 8; swizzled = true; break;
// case 0x72: texture_format = DDS_FORMAT_????; bpp = ?; break;
default:
fprintf(stderr, "ERROR: Unsupported texture type (0x%02X)\n", tex->type);
fprintf(stderr, "Please visit: https://github.com/VitaSmith/gust_tools/issues/51\n");
continue;
}
uint32_t highest_mipmap_size = (width * height * bpp) / 8;
Expand All @@ -846,11 +864,6 @@ int main_utf8(int argc, char** argv)
uint32_t expected_size = ((i + 1 == hdr->nb_textures) ?
g1t_size - hdr->header_size : getv32(x_offset_table[i + 1])) - getv32(x_offset_table[i]);
expected_size -= (uint32_t)sizeof(g1t_tex_header);
if (texture_size > expected_size) {
fprintf(stderr, "ERROR: Computed texture size is larger than actual size\n");
continue;
}

if (flags & G1T_FLAG_LOCAL_XDATA) {
assert(pos + extra_size < g1t_size);
if ((extra_size < 8) || (extra_size % 4 != 0)) {
Expand All @@ -865,15 +878,28 @@ int main_utf8(int argc, char** argv)
pos += extra_size;
expected_size -= extra_size;
}
texture_size = expected_size;
if (texture_size > expected_size) {
fprintf(stderr, "ERROR: Computed texture size is larger than actual size\n");
continue;
}
if (texture_size < expected_size) {
if (expected_size % texture_size != 0)
fprintf(stderr, "ERROR: Extended texture %08x is not a multiple of base size %08x\n",
expected_size, texture_size);
z_stack = expected_size / texture_size;
json_object_set_number(json_object(json_texture), "z_stack", z_stack);
texture_size = expected_size;
}

snprintf(path, sizeof(path), "%s%s%c%03d.dds", dir, _basename(argv[argc - 1]), PATH_SEP, i);
char dims[16];
snprintf(dims, sizeof(dims), "%dx%d", width, height);
printf("0x%02x 0x%08x 0x%08x %s %-10s %-7d %s\n", tex->type, hdr->header_size + x_offset_table[i],
expected_size, &path[strlen(dir)], dims, tex->mipmaps, supported ? "Y" : "N");
if (list_only)
printf("0x%02x 0x%08x 0x%08x %s %-10s %-7d %-6d\n", tex->type, hdr->header_size + x_offset_table[i],
expected_size, &path[strlen(dir)], dims, tex->mipmaps, z_stack);
if (list_only) {
json_value_free(json_texture);
continue;
}
FILE* dst = fopen_utf8(path, "wb");
if (dst == NULL) {
fprintf(stderr, "ERROR: Can't create file '%s'\n", path);
Expand All @@ -885,7 +911,7 @@ int main_utf8(int argc, char** argv)
fclose(dst);
continue;
}
if (write_dds_header(dst, texture_format, width, height, bpp,
if (write_dds_header(dst, texture_format, width, height * z_stack, bpp,
tex->mipmaps, flags) != 1) {
fprintf(stderr, "ERROR: Can't write DDS header\n");
fclose(dst);
Expand Down Expand Up @@ -950,6 +976,8 @@ int main_utf8(int argc, char** argv)
json_object_set_value(json_object(json), "extra_flags", json_extra_flags_array);
if (hdr->extra_size)
json_object_set_value(json_object(json), "extra_data", json_global_extra_data_array);
else
json_value_free(json_global_extra_data_array);
json_object_set_value(json_object(json), "textures", json_textures_array);
snprintf(path, sizeof(path), "%s%cg1t.json", argv[argc - 1], PATH_SEP);
if (!list_only)
Expand Down
2 changes: 2 additions & 0 deletions test_g1t.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ set list=^
type_59_win_2^
type_5b_win^
type_5b_win_2^
type_5b_win_3^
type_5c_win^
type_5f_win^
type_5f_win_2^
type_60_ps4^
Expand Down

0 comments on commit 74192fb

Please sign in to comment.