summaryrefslogtreecommitdiff
path: root/blpconvert.c
diff options
context:
space:
mode:
Diffstat (limited to 'blpconvert.c')
-rw-r--r--blpconvert.c434
1 files changed, 0 insertions, 434 deletions
diff --git a/blpconvert.c b/blpconvert.c
deleted file mode 100644
index f4a324f..0000000
--- a/blpconvert.c
+++ /dev/null
@@ -1,434 +0,0 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <stdint.h>
4#include <string.h>
5#include <stdbool.h>
6#include <stdarg.h>
7#include <getopt.h>
8#include <libgen.h>
9
10#define STB_IMAGE_WRITE_IMPLEMENTATION
11#include "stb_image_write.h"
12
13struct blp2header {
14 uint8_t ident[4]; // "BLP2" magic number
15 uint32_t type; // 0 = JPG, 1 = BLP / DXTC / Uncompressed
16 uint8_t compression; // 1 = BLP, 2 = DXTC, 3 = Uncompressed
17 uint8_t alpha_depth; // 0, 1, 4, or 8
18 uint8_t alpha_type; // 0, 1, 7, or 8
19 uint8_t has_mips; // 0 = no mips, 1 = has mips
20 uint32_t width; // Image width in pixels
21 uint32_t height; // Image height in pixels
22 uint32_t mipmap_offsets[16]; // File offsets of each mipmap
23 uint32_t mipmap_lengths[16]; // Length of each mipmap data block
24 uint32_t palette[256]; // Color palette (256 ARGB values)
25} __attribute__((packed)); // Ensure no padding issues
26
27typedef struct {
28 const char *fullname;
29 char *folder;
30 char *filename; // Filename without extension
31 char *extension; // File extension (including the dot)
32} path_components;
33
34const char *type_labels[] = {
35 "JPG", // Index 0
36 "BLP/DXTC/Uncompressed" // Index 1
37};
38
39const char *compression_labels[] = {
40 "Invalid", // Index 0 (Unused)
41 "BLP", // Index 1
42 "DXTC", // Index 2
43 "Uncompressed" // Index 3
44};
45
46path_components extract_path_components(const char *filepath) {
47 path_components result;
48 result.fullname = filepath;
49 char *filepath_copy1 = strdup(filepath);
50 char *filepath_copy2 = strdup(filepath);
51
52 result.folder = strdup(dirname(filepath_copy1));
53
54 char *full_filename = basename(filepath_copy2);
55 char *last_dot = strrchr(full_filename, '.');
56
57 if (last_dot != NULL && last_dot != full_filename) {
58 size_t name_length = last_dot - full_filename;
59 result.filename = (char *)malloc(name_length + 1);
60 strncpy(result.filename, full_filename, name_length);
61 result.filename[name_length] = '\0';
62 result.extension = strdup(last_dot);
63 } else {
64 result.filename = strdup(full_filename);
65 result.extension = strdup("");
66 }
67
68 free(filepath_copy1);
69 free(filepath_copy2);
70
71 return result;
72}
73
74void free_path_components(path_components *path) {
75 free(path->folder);
76 free(path->filename);
77 free(path->extension);
78}
79
80void dxt1_to_rgba(const uint8_t* dxt1_block, uint8_t* rgba_pixels) {
81 uint16_t color0 = (dxt1_block[0] | (dxt1_block[1] << 8));
82 uint16_t color1 = (dxt1_block[2] | (dxt1_block[3] << 8));
83 uint32_t color_bits = (dxt1_block[4] | (dxt1_block[5] << 8) | (dxt1_block[6] << 16) | (dxt1_block[7] << 24));
84
85 uint8_t r0 = ((color0 >> 11) & 0x1F) << 3;
86 uint8_t g0 = ((color0 >> 5) & 0x3F) << 2;
87 uint8_t b0 = (color0 & 0x1F) << 3;
88
89 uint8_t r1 = ((color1 >> 11) & 0x1F) << 3;
90 uint8_t g1 = ((color1 >> 5) & 0x3F) << 2;
91 uint8_t b1 = (color1 & 0x1F) << 3;
92
93 uint8_t colors[4][4];
94
95 colors[0][0] = r0; colors[0][1] = g0; colors[0][2] = b0; colors[0][3] = 255;
96 colors[1][0] = r1; colors[1][1] = g1; colors[1][2] = b1; colors[1][3] = 255;
97
98 if (color0 > color1) {
99 colors[2][0] = (2 * r0 + r1) / 3;
100 colors[2][1] = (2 * g0 + g1) / 3;
101 colors[2][2] = (2 * b0 + b1) / 3;
102 colors[2][3] = 255;
103
104 colors[3][0] = (r0 + 2 * r1) / 3;
105 colors[3][1] = (g0 + 2 * g1) / 3;
106 colors[3][2] = (b0 + 2 * b1) / 3;
107 colors[3][3] = 255;
108 } else {
109 colors[2][0] = (r0 + r1) / 2;
110 colors[2][1] = (g0 + g1) / 2;
111 colors[2][2] = (b0 + b1) / 2;
112 colors[2][3] = 255;
113
114 colors[3][0] = 0;
115 colors[3][1] = 0;
116 colors[3][2] = 0;
117 colors[3][3] = 0;
118 }
119
120 for (int i = 0; i < 16; i++) {
121 uint8_t color_idx = (color_bits >> (i * 2)) & 0x3;
122 uint8_t* pixel = rgba_pixels + (i * 4);
123 pixel[0] = colors[color_idx][0];
124 pixel[1] = colors[color_idx][1];
125 pixel[2] = colors[color_idx][2];
126 pixel[3] = colors[color_idx][3];
127 }
128}
129
130void dxt3_to_rgba(const uint8_t* dxt3_block, uint8_t* rgba_pixels) {
131 // First 8 bytes contain the alpha values (4 bits per pixel).
132 uint64_t alpha_bits;
133 memcpy(&alpha_bits, dxt3_block, 8);
134
135 // Last 8 bytes contain the color data (same as DXT1 without alpha interpretation).
136 dxt1_to_rgba(dxt3_block + 8, rgba_pixels);
137
138 // Override the alpha values with the explicit values from the alpha block.
139 for (int i = 0; i < 16; i++) {
140 uint8_t alpha = ((alpha_bits >> (i * 4)) & 0xF) << 4 | ((alpha_bits >> (i * 4)) & 0xF);
141 rgba_pixels[i * 4 + 3] = alpha; // Set the alpha channel.
142 }
143}
144
145void dxt5_to_rgba(const uint8_t* dxt5_block, uint8_t* rgba_pixels) {
146 // First 8 bytes contain the interpolated alpha values.
147 uint8_t alpha0 = dxt5_block[0];
148 uint8_t alpha1 = dxt5_block[1];
149
150 // Read the 6 bytes of alpha indices (48 bits total, 3 bits per pixel).
151 uint64_t alpha_indices = 0;
152 memcpy(&alpha_indices, dxt5_block + 2, 6);
153
154 // Calculate alpha values table.
155 uint8_t alpha_table[8];
156 alpha_table[0] = alpha0;
157 alpha_table[1] = alpha1;
158
159 if (alpha0 > alpha1) {
160 // 8-alpha interpolation.
161 alpha_table[2] = (6 * alpha0 + 1 * alpha1) / 7;
162 alpha_table[3] = (5 * alpha0 + 2 * alpha1) / 7;
163 alpha_table[4] = (4 * alpha0 + 3 * alpha1) / 7;
164 alpha_table[5] = (3 * alpha0 + 4 * alpha1) / 7;
165 alpha_table[6] = (2 * alpha0 + 5 * alpha1) / 7;
166 alpha_table[7] = (1 * alpha0 + 6 * alpha1) / 7;
167 } else {
168 // 6-alpha interpolation.
169 alpha_table[2] = (4 * alpha0 + 1 * alpha1) / 5;
170 alpha_table[3] = (3 * alpha0 + 2 * alpha1) / 5;
171 alpha_table[4] = (2 * alpha0 + 3 * alpha1) / 5;
172 alpha_table[5] = (1 * alpha0 + 4 * alpha1) / 5;
173 alpha_table[6] = 0;
174 alpha_table[7] = 255;
175 }
176
177 // Decode the color data (last 8 bytes).
178 dxt1_to_rgba(dxt5_block + 8, rgba_pixels);
179
180 // Apply alpha values.
181 for (int i = 0; i < 16; i++) {
182 // Extract 3-bit alpha index.
183 int bit_pos = i * 3;
184 int byte_pos = bit_pos / 8;
185 int bit_offset = bit_pos % 8;
186
187 uint8_t alpha_index;
188 if (bit_offset > 5) {
189 // Alpha index spans two bytes.
190 alpha_index = (dxt5_block[2 + byte_pos] >> bit_offset) | ((dxt5_block[2 + byte_pos + 1] & ((1 << (bit_offset - 5)) - 1)) << (8 - bit_offset));
191 } else {
192 alpha_index = (dxt5_block[2 + byte_pos] >> bit_offset) & 0x07;
193 }
194
195 rgba_pixels[i * 4 + 3] = alpha_table[alpha_index];
196 }
197}
198
199void decode_dxt_image(const uint8_t* image_data, uint32_t width, uint32_t height, int dxt_type, path_components *path, bool verbose) {
200 uint32_t blocks_wide = (width + 3) / 4;
201 uint32_t blocks_high = (height + 3) / 4;
202 uint32_t total_pixels = width * height;
203 uint32_t block_size = (dxt_type == 1) ? 8 : 16; // DXT3/5 blocks are 16 bytes, DXT1 are 8 bytes.
204
205 uint8_t* decoded_image = (uint8_t*)malloc(total_pixels * 4);
206 if (!decoded_image) {
207 printf("Failed to allocate memory for decoded image\n");
208 return;
209 }
210
211 // Process each block.
212 for (uint32_t by = 0; by < blocks_high; by++) {
213 for (uint32_t bx = 0; bx < blocks_wide; bx++) {
214 uint8_t block_rgba[64];
215 const uint8_t* dxt_block = image_data + (by * blocks_wide + bx) * block_size;
216
217 switch (dxt_type) {
218 case 1:
219 dxt1_to_rgba(dxt_block, block_rgba);
220 break;
221 case 3:
222 dxt3_to_rgba(dxt_block, block_rgba);
223 break;
224 case 5:
225 dxt5_to_rgba(dxt_block, block_rgba);
226 break;
227 }
228
229 for (int py = 0; py < 4; py++) {
230 for (int px = 0; px < 4; px++) {
231 int x = bx * 4 + px;
232 int y = by * 4 + py;
233
234 if (x >= width || y >= height) continue;
235
236 int src_idx = (py * 4 + px) * 4;
237 int dst_idx = (y * width + x) * 4;
238
239 decoded_image[dst_idx + 0] = block_rgba[src_idx + 0];
240 decoded_image[dst_idx + 1] = block_rgba[src_idx + 1];
241 decoded_image[dst_idx + 2] = block_rgba[src_idx + 2];
242 decoded_image[dst_idx + 3] = block_rgba[src_idx + 3];
243 }
244 }
245 }
246 }
247
248 if (verbose) {
249 printf("Saving decoded image as PNG...\n");
250 }
251
252 char output_filename[512];
253 snprintf(output_filename, sizeof(output_filename), "%s/%s.png", path->folder, path->filename);
254
255 if (stbi_write_png(output_filename, width, height, 4, decoded_image, width * 4) == 0) {
256 printf("Failed to write PNG file\n");
257 } else {
258 printf("Successfully saved %s\n", output_filename);
259 }
260
261 // Print first few pixels for verification.
262 if (verbose) {
263 printf("\nFirst few pixels of decoded image (RGBA format):\n");
264 for (int y = 0; y < 4; y++) {
265 for (int x = 0; x < 4; x++) {
266 int idx = (y * width + x) * 4;
267 printf("(%3d,%3d,%3d,%3d) ",
268 decoded_image[idx],
269 decoded_image[idx + 1],
270 decoded_image[idx + 2],
271 decoded_image[idx + 3]);
272 }
273 printf("\n");
274 }
275 }
276
277 free(decoded_image);
278}
279
280void convert_blp_file(path_components *path, bool verbose) {
281 FILE *file = fopen(path->fullname, "rb");
282 if (!file) {
283 perror("Error opening file");
284 return;
285 }
286
287 struct blp2header header;
288
289 if (fread(&header, sizeof(struct blp2header), 1, file) != 1) {
290 perror("Error reading header");
291 fclose(file);
292 return;
293 }
294
295 if (memcmp(header.ident, "BLP2", 4) != 0) {
296 printf("Invalid BLP file!\n");
297 fclose(file);
298 return;
299 }
300
301 if (verbose) {
302 printf("BLP File Details:\n");
303 printf(" Type: %u, %s\n", header.type, type_labels[header.type]);
304 printf(" Compression: %u, %s\n", header.compression, compression_labels[header.compression]);
305 printf(" Alpha Depth: %u\n", header.alpha_depth);
306 printf(" Alpha Type: %u\n", header.alpha_type);
307 printf(" Has Mipmaps: %u\n", header.has_mips);
308 printf(" Width: %u, Height: %u\n", header.width, header.height);
309 }
310
311 // Determine image data location.
312 uint32_t offset = header.mipmap_offsets[0]; // First mipmap (highest resolution)
313 uint32_t length = header.mipmap_lengths[0]; // Length of the mipmap
314
315 if (offset == 0 || length == 0) {
316 printf("No image data found.\n");
317 fclose(file);
318 return;
319 }
320
321 if (verbose) {
322 printf("Reading image data at offset %u, size %u bytes\n", offset, length);
323 }
324
325 // Allocate buffer and read image data.
326 uint8_t *image_data = (uint8_t *)malloc(length);
327 if (!image_data) {
328 perror("Memory allocation failed");
329 fclose(file);
330 return;
331 }
332
333 fseek(file, offset, SEEK_SET);
334 if (fread(image_data, length, 1, file) != 1) {
335 perror("Error reading image data");
336 free(image_data);
337 fclose(file);
338 return;
339 }
340
341 if (header.compression == 2) {
342 if (verbose) {
343 printf("BLP is compressed with DXTC.\n");
344 printf("Image has %d bytes.\n", length);
345 }
346
347 switch (header.alpha_type) {
348 case 0: // DXT1
349 decode_dxt_image(image_data, header.width, header.height, 1, path, verbose);
350 break;
351
352 case 1: // DXT3
353 decode_dxt_image(image_data, header.width, header.height, 3, path, verbose);
354 break;
355
356 case 7: // DXT5
357 decode_dxt_image(image_data, header.width, header.height, 5, path, verbose);
358 break;
359
360 default:
361 printf("Unsupported alpha type: %d\n", header.alpha_type);
362 break;
363 }
364
365 }
366
367 fclose(file);
368}
369
370void print_help(const char *program_name) {
371 printf("Usage: %s [OPTIONS] file1 [file2 ...]\n\n", program_name);
372 printf("Options:\n");
373 printf(" -h, --help Display this help message\n");
374 printf(" -v, --verbose Enable verbose output\n");
375}
376
377
378int main(int argc, char *argv[]) {
379 bool verbose = false;
380 int c;
381
382 static struct option long_options[] = {
383 {"help", no_argument, 0, 'h'},
384 {"verbose", no_argument, 0, 'v'},
385 {0, 0, 0, 0}
386 };
387
388 // Parse command line options
389 while (1) {
390 int option_index = 0;
391 c = getopt_long(argc, argv, "hv", long_options, &option_index);
392 if (c == -1) { break; }
393
394 switch (c) {
395 case 'h':
396 print_help(argv[0]);
397 return 0;
398 case 'v':
399 verbose = true;
400 break;
401 case '?':
402 return 1;
403 default:
404 abort();
405 }
406 }
407
408 // No files specified
409 if (optind >= argc) {
410 fprintf(stderr, "Error: No input files specified\n");
411 print_help(argv[0]);
412 return 1;
413 }
414
415
416 // Loop though all provided files.
417 while (optind < argc) {
418 path_components path = extract_path_components(argv[optind++]);
419
420 if (verbose) {
421 printf("Processing File:\n");
422 printf(" Fullname: %s\n", path.fullname);
423 printf(" Folder: %s\n", path.folder);
424 printf(" Filename: %s\n", path.filename);
425 printf(" Extension: %s\n", path.extension);
426 }
427
428 convert_blp_file(&path, verbose);
429 free_path_components(&path);
430 }
431
432 return 0;
433}
434