Autor Tema: Conversor yytext to png y viceversa  (Leído 741 veces)

Fl0ppy

  • Administrador
  • Usuario Héroe
  • *****
  • Mensajes: 10370
Conversor yytext to png y viceversa
« en: 19 de Julio de 2025, 02:52:42 am »
Lo podeis probar con el juego Xeno.Rangers.multi8 *REPACK LOSSY*

Código en C
Spoiler for Hiden:
//BASED IN THE CODE
//https://github.com/FeiZhaixiage/yytexUnpacker
//
//BUILD 19-07-2025
//REMOVED BZIP2 FOR COMPRESSION , NOW THE TEXCTURES CAN BE LOADED

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
#include <bzlib.h>

#define QOI_INDEX   0x00
#define QOI_RUN_8   0x40
#define QOI_RUN_16  0x60
#define QOI_DIFF_8  0x80
#define QOI_DIFF_16 0xc0
#define QOI_DIFF_24 0xe0
#define QOI_COLOR   0xf0
#define QOI_MASK_2  0xc0
#define QOI_MASK_3  0xe0
#define QOI_MASK_4  0xf0

typedef struct {
    unsigned char *data;
    int width;
    int height;
    int channels;
} Image;

// Read entire file into memory
unsigned char* read_file(const char *filename, size_t *size) {
    FILE *file = fopen(filename, "rb");
    if (!file) {
        fprintf(stderr, "Cannot open file: %s\n", filename);
        return NULL;
    }
    fseek(file, 0, SEEK_END);
    *size = ftell(file);
    fseek(file, 0, SEEK_SET);
    unsigned char *buffer = (unsigned char*)malloc(*size);
    if (!buffer) {
        fclose(file);
        return NULL;
    }
    fread(buffer, 1, *size, file);
    fclose(file);
    return buffer;
}

// Write data to file
int write_file(const char *filename, unsigned char *data, size_t data_len) {
    FILE *file = fopen(filename, "wb");
    if (!file) {
        fprintf(stderr, "Cannot open output file: %s\n", filename);
        return 0;
    }
    size_t written = fwrite(data, 1, data_len, file);
    fclose(file);
    if (written != data_len) {
        fprintf(stderr, "Failed to write all data to file: %s\n", filename);
        return 0;
    }
    return 1;
}

// Decompress BZip2 data, skipping 12-byte header
unsigned char* decompress_bzip2(const char *filename, size_t *out_len) {
    size_t file_size;
    unsigned char *file_data = read_file(filename, &file_size);
    if (!file_data || file_size <= 12) {
        fprintf(stderr, "File too short or cannot be read: %s\n", filename);
        free(file_data);
        return NULL;
    }

    unsigned char *compressed_data = file_data + 12;
    size_t compressed_len = file_size - 12;

    bz_stream strm = {0};
    if (BZ2_bzDecompressInit(&strm, 0, 0) != BZ_OK) {
        fprintf(stderr, "BZip2 decompression initialization failed\n");
        free(file_data);
        return NULL;
    }

    unsigned char *decompressed = (unsigned char*)malloc(compressed_len * 2);
    size_t decompressed_size = compressed_len * 2;
    size_t decompressed_pos = 0;

    strm.next_in = (char*)compressed_data;
    strm.avail_in = compressed_len;
    strm.next_out = (char*)decompressed;
    strm.avail_out = decompressed_size;

    while (1) {
        int bz_result = BZ2_bzDecompress(&strm);
        if (bz_result == BZ_STREAM_END) {
            decompressed_pos = decompressed_size - strm.avail_out;
            break;
        }
        if (bz_result != BZ_OK) {
            fprintf(stderr, "BZip2 decompression error\n");
            BZ2_bzDecompressEnd(&strm);
            free(decompressed);
            free(file_data);
            return NULL;
        }

        if (strm.avail_out == 0) {
            decompressed_size *= 2;
            decompressed = (unsigned char*)realloc(decompressed, decompressed_size);
            strm.next_out = (char*)(decompressed + decompressed_pos);
            strm.avail_out = decompressed_size - decompressed_pos;
        }
    }

    *out_len = decompressed_pos;
    BZ2_bzDecompressEnd(&strm);
    free(file_data);
    return decompressed;
}

// Load PNG using libpng
Image* load_png(const char *filename) {
    FILE *file = fopen(filename, "rb");
    if (!file) {
        fprintf(stderr, "Cannot open PNG file: %s\n", filename);
        return NULL;
    }

    png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png) {
        fclose(file);
        return NULL;
    }
    png_infop info = png_create_info_struct(png);
    if (!info) {
        png_destroy_read_struct(&png, NULL, NULL);
        fclose(file);
        return NULL;
    }

    if (setjmp(png_jmpbuf(png))) {
        png_destroy_read_struct(&png, &info, NULL);
        fclose(file);
        return NULL;
    }

    png_init_io(png, file);
    png_read_info(png, info);

    int width = png_get_image_width(png, info);
    int height = png_get_image_height(png, info);
    png_byte color_type = png_get_color_type(png, info);
    png_byte bit_depth = png_get_bit_depth(png, info);

    if (bit_depth == 16) png_set_strip_16(png);
    if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png);
    if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
        png_set_gray_to_rgb(png);
    }
    if (!(color_type & PNG_COLOR_MASK_ALPHA)) png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER);
    png_set_bgr(png); // Match C# ARGB order (BGRA in memory)
    png_read_update_info(png, info);

    Image *img = (Image*)malloc(sizeof(Image));
    img->width = width;
    img->height = height;
    img->channels = 4;
    img->data = (unsigned char*)malloc(width * height * 4);
    if (!img->data) {
        png_destroy_read_struct(&png, &info, NULL);
        fclose(file);
        free(img);
        return NULL;
    }

    png_bytep *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
    for (int y = 0; y < height; y++) {
        row_pointers[y] = img->data + y * width * 4;
    }

    png_read_image(png, row_pointers);
    png_read_end(png, NULL);
    png_destroy_read_struct(&png, &info, NULL);
    free(row_pointers);
    fclose(file);
    return img;
}

// Save PNG using libpng
int save_png(Image *img, const char *filename) {
    FILE *file = fopen(filename, "wb");
    if (!file) {
        fprintf(stderr, "Cannot open output file: %s\n", filename);
        return 0;
    }

    png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png) {
        fclose(file);
        return 0;
    }
    png_infop info = png_create_info_struct(png);
    if (!info) {
        png_destroy_write_struct(&png, NULL);
        fclose(file);
        return 0;
    }

    if (setjmp(png_jmpbuf(png))) {
        png_destroy_write_struct(&png, &info);
        fclose(file);
        return 0;
    }

    png_init_io(png, file);
    png_set_IHDR(png, info, img->width, img->height, 8, PNG_COLOR_TYPE_RGBA,
                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    png_set_bgr(png); // Match input format
    png_write_info(png, info);

    png_bytep *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * img->height);
    for (int y = 0; y < img->height; y++) {
        row_pointers[y] = img->data + y * img->width * 4;
    }

    png_write_image(png, row_pointers);
    png_write_end(png, NULL);
    png_destroy_write_struct(&png, &info);
    free(row_pointers);
    fclose(file);
    return 1;
}

// QOI Decoder
Image* qoi_get_image_from_data(unsigned char *data, size_t data_len) {
    if (data_len < 12) {
        fprintf(stderr, "Data too short: %zu bytes\n", data_len);
        return NULL;
    }
    fprintf(stderr, "Magic bytes: %c%c%c%c (0x%02x 0x%02x 0x%02x 0x%02x)\n",
            data[0], data[1], data[2], data[3],
            data[0], data[1], data[2], data[3]);
    if (data[0] != 'f' || data[1] != 'i' || data[2] != 'o' || data[3] != 'q') {
        fprintf(stderr, "Invalid QOIF image magic\n");
        return NULL;
    }

    int width = data[4] | (data[5] << 8);
    int height = data[6] | (data[7] << 8);
    int length = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);

    if (data_len < (size_t)length + 12) {
        fprintf(stderr, "Invalid QOI data length\n");
        return NULL;
    }

    Image *img = (Image*)malloc(sizeof(Image));
    img->width = width;
    img->height = height;
    img->channels = 4;
    img->data = (unsigned char*)malloc(width * height * 4);
    if (!img->data) {
        free(img);
        return NULL;
    }

    unsigned char *bmp_ptr = img->data;
    unsigned char *bmp_end = bmp_ptr + (width * height * 4);
    size_t pos = 12;
    int run = 0;
    unsigned char r = 0, g = 0, b = 0, a = 255;
    unsigned char index[64 * 4] = {0};

    while (bmp_ptr < bmp_end && pos < data_len) {
        if (run > 0) {
            run--;
        } else {
            int b1 = data[pos++];

            if ((b1 & QOI_MASK_2) == QOI_INDEX) {
                int index_pos = (b1 ^ QOI_INDEX) << 2;
                r = index[index_pos];
                g = index[index_pos + 1];
                b = index[index_pos + 2];
                a = index[index_pos + 3];
            } else if ((b1 & QOI_MASK_3) == QOI_RUN_8) {
                run = b1 & 0x1f;
            } else if ((b1 & QOI_MASK_3) == QOI_RUN_16) {
                int b2 = data[pos++];
                run = (((b1 & 0x1f) << 8) | b2) + 32;
            } else if ((b1 & QOI_MASK_2) == QOI_DIFF_8) {
                r += (unsigned char)(((b1 & 48) << 26 >> 30) & 0xff);
                g += (unsigned char)(((b1 & 12) << 28 >> 22 >> 8) & 0xff);
                b += (unsigned char)(((b1 & 3) << 30 >> 14 >> 16) & 0xff);
            } else if ((b1 & QOI_MASK_3) == QOI_DIFF_16) {
                int b2 = data[pos++];
                int merged = b1 << 8 | b2;
                r += (unsigned char)(((merged & 7936) << 19 >> 27) & 0xff);
                g += (unsigned char)(((merged & 240) << 24 >> 20 >> 8) & 0xff);
                b += (unsigned char)(((merged & 15) << 28 >> 12 >> 16) & 0xff);
            } else if ((b1 & QOI_MASK_4) == QOI_DIFF_24) {
                int b2 = data[pos++];
                int b3 = data[pos++];
                int merged = b1 << 16 | b2 << 8 | b3;
                r += (unsigned char)(((merged & 1015808) << 12 >> 27) & 0xff);
                g += (unsigned char)(((merged & 31744) << 17 >> 19 >> 8) & 0xff);
                b += (unsigned char)(((merged & 992) << 22 >> 11 >> 16) & 0xff);
                a += (unsigned char)(((merged & 31) << 27 >> 3 >> 24) & 0xff);
            } else if ((b1 & QOI_MASK_4) == QOI_COLOR) {
                if (b1 & 8) r = data[pos++];
                if (b1 & 4) g = data[pos++];
                if (b1 & 2) b = data[pos++];
                if (b1 & 1) a = data[pos++];
            }

            int index_pos = ((r ^ g ^ b ^ a) & 63) << 2;
            index[index_pos] = r;
            index[index_pos + 1] = g;
            index[index_pos + 2] = b;
            index[index_pos + 3] = a;
        }

        *bmp_ptr++ = b;
        *bmp_ptr++ = g;
        *bmp_ptr++ = r;
        *bmp_ptr++ = a;
    }

    return img;
}

// QOI Encoder
unsigned char* qoi_get_array_from_image(Image *img, size_t *out_len) {
    size_t max_size = (img->width * img->height * 5) + 12;
    unsigned char *res = (unsigned char*)malloc(max_size);
    if (!res) return NULL;

    res[0] = 'f';
    res[1] = 'i';
    res[2] = 'o';
    res[3] = 'q';
    res[4] = (unsigned char)(img->width & 0xff);
    res[5] = (unsigned char)((img->width >> 8) & 0xff);
    res[6] = (unsigned char)(img->height & 0xff);
    res[7] = (unsigned char)((img->height >> 8) & 0xff);

    size_t res_pos = 12;
    unsigned char r = 0, g = 0, b = 0, a = 255;
    int run = 0;
    int v = 0, v_prev = 0xff;
    int index[64] = {0};

    unsigned char *bmp_ptr = img->data;
    unsigned char *bmp_end = bmp_ptr + (img->width * img->height * 4);

    while (bmp_ptr < bmp_end) {
        b = *bmp_ptr++;
        g = *bmp_ptr++;
        r = *bmp_ptr++;
        a = *bmp_ptr++;

        v = (r << 24) | (g << 16) | (b << 8) | a;
        if (v == v_prev) {
            run++;
            if (run == 0x2020) {
                if (run < 33) {
                    run -= 1;
                    res[res_pos++] = (unsigned char)(QOI_RUN_8 | run);
                } else {
                    run -= 33;
                    res[res_pos++] = (unsigned char)(QOI_RUN_16 | (run >> 8));
                    res[res_pos++] = (unsigned char)run;
                }
                run = 0;
            }
            continue;
        }

        if (run > 0) {
            if (run < 33) {
                run -= 1;
                res[res_pos++] = (unsigned char)(QOI_RUN_8 | run);
            } else {
                run -= 33;
                res[res_pos++] = (unsigned char)(QOI_RUN_16 | (run >> 8));
                res[res_pos++] = (unsigned char)run;
            }
            run = 0;
        }

        int index_pos = (r ^ g ^ b ^ a) & 63;
        if (index[index_pos] == v) {
            res[res_pos++] = (unsigned char)(QOI_INDEX | index_pos);
        } else {
            index[index_pos] = v;

            int vr = r - ((v_prev >> 24) & 0xff);
            int vg = g - ((v_prev >> 16) & 0xff);
            int vb = b - ((v_prev >> 8) & 0xff);
            int va = a - (v_prev & 0xff);

            if (vr > -17 && vr < 16 &&
                vg > -17 && vg < 16 &&
                vb > -17 && vb < 16 &&
                va > -17 && va < 16) {
                if (va == 0 &&
                    vr > -3 && vr < 2 &&
                    vg > -3 && vg < 2 &&
                    vb > -3 && vb < 2) {
                    res[res_pos++] = (unsigned char)(QOI_DIFF_8 | ((vr & 3) << 4) | ((vg & 3) << 2) | (vb & 3));
                } else if (va == 0 &&
                           vg > -9 && vg < 8 &&
                           vb > -9 && vb < 8) {
                    res[res_pos++] = (unsigned char)(QOI_DIFF_16 | (vr & 31));
                    res[res_pos++] = (unsigned char)(((vg & 15) << 4) | (vb & 15));
                } else {
                    res[res_pos++] = (unsigned char)(QOI_DIFF_24 | ((vr >> 1) & 15));
                    res[res_pos++] = (unsigned char)(((vr << 7) & 128) | ((vg << 2) & 124) | ((vb >> 3) & 3));
                    res[res_pos++] = (unsigned char)(((vb << 5) & 224) | (va & 31));
                }
            } else {
                res[res_pos++] = (unsigned char)(QOI_COLOR | (vr != 0 ? 8 : 0) | (vg != 0 ? 4 : 0) | (vb != 0 ? 2 : 0) | (va != 0 ? 1 : 0));
                if (vr != 0) res[res_pos++] = r;
                if (vg != 0) res[res_pos++] = g;
                if (vb != 0) res[res_pos++] = b;
                if (va != 0) res[res_pos++] = a;
            }
        }

        v_prev = v;
    }

    if (run > 0) {
        if (run < 33) {
            run -= 1;
            res[res_pos++] = (unsigned char)(QOI_RUN_8 | run);
        } else {
            run -= 33;
            res[res_pos++] = (unsigned char)(QOI_RUN_16 | (run >> 8));
            res[res_pos++] = (unsigned char)run;
        }
    }

    *out_len = res_pos;
    unsigned char *output = (unsigned char*)malloc(res_pos);
    memcpy(output, res, res_pos);
    free(res);
    return output;
}

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage:\n");
        printf("  To unpack .yytex → .png: %s unpack <file.yytex>\n", argv[0]);
        printf("  To pack   .png   → .yytex: %s pack <file.png>\n", argv[0]);
        return 1;
    }

    const char *mode = argv[1];
    const char *input_filepath = argv[2];
    char output_filepath[256];
    snprintf(output_filepath, sizeof(output_filepath), "%s", input_filepath);

    FILE *test_file = fopen(input_filepath, "rb");
    if (!test_file) {
        fprintf(stderr, "File does not exist: %s\n", input_filepath);
        return 1;
    }
    fclose(test_file);

    if (strcmp(mode, "unpack") == 0) {
        output_filepath[strlen(output_filepath) - 6] = '\0';
        strcat(output_filepath, ".png");

        size_t decompressed_len;
        unsigned char *decompressed_data = decompress_bzip2(input_filepath, &decompressed_len);
        if (!decompressed_data) {
            fprintf(stderr, "Decompression failed\n");
            return 1;
        }

        Image *img = qoi_get_image_from_data(decompressed_data, decompressed_len);
        free(decompressed_data);
        if (!img) {
            fprintf(stderr, "QOI decoding failed\n");
            return 1;
        }

        if (!save_png(img, output_filepath)) {
            fprintf(stderr, "Failed to save PNG\n");
            free(img->data);
            free(img);
            return 1;
        }
        free(img->data);
        free(img);
        printf("Unpacked and saved PNG to: %s\n", output_filepath);
    } else if (strcmp(mode, "pack") == 0) {
        output_filepath[strlen(output_filepath) - 4] = '\0';
        strcat(output_filepath, ".yytex");

        Image *img = load_png(input_filepath);
        if (!img) {
            fprintf(stderr, "Failed to load PNG: %s\n", input_filepath);
            return 1;
        }

        size_t qoi_len;
        unsigned char *qoi_data = qoi_get_array_from_image(img, &qoi_len);
        free(img->data);
        free(img);
        if (!qoi_data) {
            fprintf(stderr, "QOI encoding failed\n");
            return 1;
        }

        if (!write_file(output_filepath, qoi_data, qoi_len)) {
            fprintf(stderr, "Failed to save .yytex\n");
            free(qoi_data);
            return 1;
        }
        free(qoi_data);
        printf("Packed and saved .yytex to: %s\n", output_filepath);
    } else {
        printf("Invalid mode. Use 'pack' or 'unpack'.\n");
        return 1;
    }

    return 0;
}

Adjunto el código y los binarios compilados para windows y GNU/Linux x86_64
Siempre que pasa igual sucede lo mismo