-
Notifications
You must be signed in to change notification settings - Fork 0
/
nintendo_tpl_gamecube.bt
262 lines (229 loc) · 13.5 KB
/
nintendo_tpl_gamecube.bt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
//------------------------------------------------
//--- 010 Editor v14.0 Binary Template
//
// File: Nintendo TPL texture palette (template?) format for the GameCube
// Authors: Swyter
// Version: 2023.10.22
// Category: Game
// File Mask: *.tpl
// ID Bytes: 00 20 AF 30
//------------------------------------------------
BigEndian(); ThemeAutoScaleColors(false); /* swy: don't wash out colors in dark mode, we want the faithful value */
uint32 magic <format=hex, bgcolor=cDkAqua>; /* swy: 00 20 AF 30 */
uint32 texture_count;
uint32 tpl_header_size; /* swy: always 12, seemingly */
local uint saved_offset, saved_offset_tex_data;
struct
{
uint32 texture_desc_pos_offset <format=hex>; /* swy: absolute start positions of each structure, or 0 if they are missing (i.e. no palette section because it's unneeded) */
uint32 palette_pos_offset <format=hex>;
saved_offset = FTell();
if (palette_pos_offset)
{
FSeek(palette_pos_offset);
struct
{
uint16 pal_color_count;
uint16 unk_always_zero;
enum <uint32>
{
FMT_PAL_IA8 = 0, /* swy: ??? */
FMT_PAL_RGB565 = 1,
FMT_PAL_RGB5A3 = 2,
FMT_PAL_3 = 3,
FMT_PAL_4 = 4,
} pal_color_format;
uint32 sum_size;
if (pal_color_count == 16384) ushort stuff[2080 / 2]; /* swy: ??? */
else if (pal_color_count == 256) ushort stuff[ 64 / 2];
else if (pal_color_count == 16) ushort stuff[ 34 / 2];
} palette[texture[0].palette_pos_offset ? 1 : 0] <open=true, optimize=false>;
}
else /* swy: HACK: ignore texture_desc_pos_offset for now if texture_descriptor goes right after a palette; doesn't seem to be correct most of the time in that case, but the palette ofset always is, and this goes afterwards */
{
Printf("[dbg] desc offset: %x; computed: %x\n", texture_desc_pos_offset, FTell() + (sizeof(texture) * texture_count));
FSeek(texture_desc_pos_offset);
}
struct
{
uint16 layer_height,
layer_width;
enum <uint32>
{
FMT_I4 = 0, /* swy: Greyscale */ /* 4 bits per pixel */
FMT_I8 = 1, /* swy: Greyscale */ /* 8 bits per pixel */
FMT_IA4 = 2, /* swy: Greyscale + Alpha */ /* 8 bits per pixel */
FMT_IA8 = 3, /* swy: Greyscale + Alpha */ /* 16 bits per pixel */
FMT_RGB565 = 4, /* swy: 565 ; R5G6B5 */ /* 16 bits per pixel */
FMT_RGB4A3 = 5, /* swy: 4443; R4G4B4A3 */ /* 16 bits per pixel */
FMT_RGBA8 = 6, /* swy: 8888; R8G8B8A8 */ /* 32 bits per pixel */
FMT_7 = 7,
FMT_C4 = 8, /* swy: palette, 16 colours (RGB565-only in Euroland) */ /* 4-bit color indices referencing a color table, whose actual RGB format is described in the palette */
FMT_C8 = 9, /* swy: palette, 256 colours (RGB565-only in Euroland) */ /* 8-bit color indices */
FMT_C14X2 = 10, /* swy: palette, 16384 colours (RGB565-only in Euroland) */ /* 14-bit color indices in 16-bit/2-bytes with 2 wasted bits; weird format that was unimplemented in Euroland and mapped to S3TC, D3D doesn't have a D3DFMT_P16, only D3DFMT_P8 */
FMT_11 = 11,
FMT_12 = 12,
FMT_13 = 13,
FMT_S3TC = 14, /* swy: S3 Texture Compression, same as DXT1 or BC1, 4x4 texel blocks, 4-bit per texel, two interpolated RGB565 colors (turning them into a palette of 3+alpha or 4 indexed colors) with optionally 1-bit cutout alpha */
FMT_15 = 15,
FMT_16 = 16,
} texel_format;
uint32 texture_data_pos_offset <format=hex>; /* swy: absolute start position in the binary file of the pixels themselves */
enum <uint32>
{
coords_clamp,
coords_repeat
} wrap_s, wrap_t; /* swy: when wrap is on the texture tiles in that UV (same as ST) space dimension, otherwise the border/edge color will appear forever */
uint32 unk_texel_fmt_result;
uint32 unk_always_one_uint;
uint32 unk_always_zero_uint;
ubyte unk_always_zero_byte;
ubyte base_lod, last_lod;
ubyte pad_garbage_data_maybe;
} texture_descriptor <open=true>;
/* swy: align the texture data to 64-byte sectors for DMA or caching reasons */
ubyte align_garbage_data[((FTell() + 32-1) & ~(32-1)) - FTell()] <bgcolor=cRed>;
// swy: HACK: FIXME: bad offset in some Euroland-generated headers :(
// FSeek(texture_descriptor.texture_data_pos_offset);
local uint mipmap_count = (texture_descriptor.last_lod - texture_descriptor.base_lod) + 1;
local int i; local uint w, h;
w = texture_descriptor.layer_width,
h = texture_descriptor.layer_height;
local float bpp = 2; /* swy: bytes per pixel; 1 byte = 8 bits */
local uint align_to_blocks_of = 64;
for (i = texture_descriptor.base_lod; i <= texture_descriptor.last_lod; i++)
{
struct
{
switch (texture_descriptor.texel_format)
{
case FMT_I4:
case FMT_C4:
case FMT_S3TC:
bpp = 0.5;
break;
case FMT_I8:
case FMT_IA4:
case FMT_C8:
bpp = 1;
break;
case FMT_IA8:
case FMT_RGB565:
case FMT_RGB4A3:
case FMT_C14X2:
bpp = 2;
break;
case FMT_RGBA8:
bpp = 4;
break;
default:
Warning("FIXME: Unhandled format\n");
}
switch (texture_descriptor.texel_format)
{
case FMT_I4:
case FMT_I8:
case FMT_IA4:
case FMT_IA8:
case FMT_C4:
case FMT_C8:
case FMT_RGB565:
case FMT_RGB4A3:
case FMT_C14X2:
align_to_blocks_of = 32;
break;
case FMT_S3TC:
case FMT_RGBA8:
align_to_blocks_of = 64;
break;
default:
Warning("FIXME: Unhandled format\n");
}
Printf("%u: %u x %u x %g\n", i, w, h, bpp);
saved_offset_tex_data = FTell();
if (texture_descriptor.texel_format == FMT_RGB4A3)
struct
{
union
{
struct { ushort flag_as_opaque: 1, r: 5, g: 5, b: 5; } opaque; /* swy: when flag bit is one we don't need to waste bits on a fully-opaque texel, so we give one extra bit of depth to each RGB channel; less banding */
struct { ushort flag_as_opaque: 1, a: 3, r: 4, g: 4, b: 4; } transl; /* swy: when flag bit is zero it's translucent, so we need to store the alpha factor */
local uint red, green, blue, alpha;
if (opaque.flag_as_opaque) /* swy: unpack/convert back each reduced channel into full 8-bit color */
{
red = (opaque.r << 3) | (opaque.r >> 2); /* swy: repeat the top-most three bits of the 5-bit field to fill out the less-significant part at the end of the target 8-bit color */
green = (opaque.g << 3) | (opaque.g >> 2); /* i.e. [12345][123] */
blue = (opaque.b << 3) | (opaque.b >> 2);
alpha = 0xFF;
}
else
{
red = (transl.r << 4) | (transl.r >> 0); /* swy: repeat the four bits twice to cover/fill out the eight-bit area [1234][1234], that way 0b1111 ends up being 0b11111111 instead of 0b11110000, which is only half the brightness */
green = (transl.g << 4) | (transl.g >> 0);
blue = (transl.b << 4) | (transl.b >> 0);
alpha = (transl.a << 5) | (transl.a << 2) | (transl.a >> 1); /* swy: repeat the three bits a bunch of times to cover/fill out the 8 bit area so that whites are full brightness [123][123][12] */
};
local uint combined = (((0xFF - alpha) << (8*3)) | (blue << (8*2)) | (green << (8*1)) | (red << (8*0))); /* swy: encode them in the ARGB8 color format for 010 Editor */
} texel <bgcolor=combined>; // | ((opaque.g << 3 | opaque.g >> 2) << (8*1)) | (opaque.r << 3 | opaque.r >> 2) << (8*0)), optimize=false>;
} mipmap_data_rgb4a3[w * h] <optimize=false>;
else if (texture_descriptor.texel_format == FMT_S3TC)
{
struct
{
ushort color_a, color_b; struct { uint a_0: 2, a_1: 2, a_2: 2, a_3: 2, /* swy: two RGB565 colors, 4 rows of 4 texels, each a 2-bit palette index (4 possible colors in the palette, each remixed from the 2 provided colors, if the two color field values are swapped ((ushort) color_b > (ushort) color_a) we signal that the last (index 3) palette color is fully transparent) */
b_0: 2, b_1: 2, b_2: 2, b_3: 2,
c_0: 2, c_1: 2, c_2: 2, c_3: 2,
d_0: 2, d_1: 2, d_2: 2, d_3: 2; } palette_indices;
} mipmap_data_s3tc[((w * h) + ((4*4) - 1)) / (4*4)] <optimize=false>; /* swy: align up in multiples of 4x4, which is the minimum block size here, even if we only want 1x1 or 2x2 */
}
else if (texture_descriptor.texel_format == FMT_IA4)
{
struct
{
ubyte alpha : 4, intensity : 4;
local uint full_alpha = alpha << 4 | alpha,
full_intensity = intensity << 4 | intensity;
local uint combined = (((0xFF - full_alpha) << (8*3)) | (full_intensity << (8*2)) | (full_intensity << (8*1)) | (full_intensity << (8*0))); /* swy: encode them in the ARGB8 color format for 010 Editor */
} mipmap_data_ia4[w * h] <optimize=false, read=Str("i: %02x / a: %02x", intensity, alpha), bgcolor=combined>;
}
else if (texture_descriptor.texel_format == FMT_I8)
{
struct
{
ubyte intensity;
local uint full_intensity = intensity;
local uint combined = ((full_intensity << (8*2)) | (full_intensity << (8*1)) | (full_intensity << (8*0))); /* swy: encode them in the ARGB8 color format for 010 Editor */
} mipmap_data_i8[w * h] <optimize=false, read=Str("i: %02x", intensity), bgcolor=combined>;
}
else if (texture_descriptor.texel_format == FMT_I4)
{
struct
{
ubyte intensity_a : 4, intensity_b : 4;
local uint full_intensity = intensity_a << 4 | intensity_a;
local uint combined = ((full_intensity << (8*2)) | (full_intensity << (8*1)) | (full_intensity << (8*0))); /* swy: encode them in the ARGB8 color format for 010 Editor */
} mipmap_data_i4[(w * h) / 2 <= 0 ? 1 : (w * h) / 2] <optimize=false, read=Str("i: %02x, i: %02x", intensity_a, intensity_b), bgcolor=combined>;
}
else if (texture_descriptor.texel_format == FMT_RGBA8)
{
struct
{
struct { ubyte a, r <bgcolor=cDkRed>; } alpha_red[16] <optimize=false>;
struct { ubyte g <bgcolor=cGreen>, b <bgcolor=cBlue>; } green_blue[16] <optimize=false>;
} mipmap_data_i4[((w * h) + 15) / 16] <optimize=false>;
}
else
ubyte mipmap_data[(w * h) * bpp];
local int data_size = (FTell() - saved_offset_tex_data);
local int data_size_aligned = (data_size + (align_to_blocks_of-1)) & ~(align_to_blocks_of-1);
if (data_size_aligned - data_size > 0)
ubyte pad_data[data_size_aligned - data_size] <bgcolor=cRed>;
/* swy: save a few useful local variables based on the changing globals inside of the struct itself */
local uint saved_i = i, saved_w = w, saved_h = h; local float saved_bpp = bpp;
/* swy: halve the size for the next mipmap in the pyramid */
w /= 2; h /= 2;
} texture_data <optimize=false, read=Str("%u: %u x %u x %g\n", saved_i, saved_w, saved_h, saved_bpp)>;
}
/* swy: go back and jump to the the next texture array element */
FSeek(saved_offset);
} texture[texture_count] <open=true, optimize=false, read=Str("[/] [%s] %u x %u (%i mipmap%s)", EnumToString(texture_descriptor.texel_format), texture_descriptor.layer_width, texture_descriptor.layer_height, mipmap_count, (mipmap_count > 2) ? "s" : "")>;