feat(573file): add texture metadata reader

This commit is contained in:
decafcode 2023-12-09 10:12:00 -05:00 committed by decafcode
parent 41ce2ef70c
commit 995c7610bb
3 changed files with 566 additions and 0 deletions

View File

@ -17,5 +17,7 @@ _573file_lib = static_library(
'prop-xml-writer.h',
'prop.c',
'prop.h',
'tex.c',
'tex.h',
]
)

524
573file/tex.c Normal file
View File

@ -0,0 +1,524 @@
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "573file/lz-file.h"
#include "573file/prop-type.h"
#include "573file/prop.h"
#include "573file/tex.h"
#include "picture/picture.h"
#include "util/crypto.h"
#include "util/hex.h"
#include "util/iobuf.h"
#include "util/list.h"
#include "util/log.h"
#include "util/macro.h"
#include "util/str.h"
static const char tex_list_prop_name[] = "texturelist";
static const char tex_texture_prop_name[] = "texture";
static const char tex_texture_size_prop_name[] = "size";
static const char tex_image_prop_name[] = "image";
static void tex_texture_free(struct tex_texture *tx);
static int tex_texture_read(struct tex_texture **out, const struct prop *p);
static int tex_texture_read_size(struct dim *dest, const struct prop *p);
static void tex_image_free(struct tex_image *ti);
static int tex_image_read(struct tex_image **out, const struct prop *p);
static int tex_image_read_rect(struct rect *rect, const struct prop *p,
const char *child_name);
int tex_list_read(struct tex_list **out, const struct prop *p) {
struct tex_list *tl;
struct tex_texture *tx;
const struct prop *child;
const char *p_name;
const char *a_compress;
int r;
assert(out != NULL);
assert(p != NULL);
*out = NULL;
tl = NULL;
p_name = prop_get_name(p);
if (!str_eq(p_name, tex_list_prop_name)) {
r = -EBADMSG;
log_write("Expected prop name \"%s\", got \"%s\"", tex_list_prop_name,
p_name);
goto end;
}
a_compress = prop_get_attr(p, "compress");
if (!str_eq(a_compress, "avslz")) {
r = -ENOTSUP;
log_write("Unknown compression format \"%s\"", a_compress);
goto end;
}
tl = calloc(1, sizeof(*tl));
if (tl == NULL) {
r = -ENOMEM;
goto end;
}
for (child = prop_get_first_child_const(p); child != NULL;
child = prop_get_next_sibling_const(child)) {
r = tex_texture_read(&tx, child);
if (r < 0) {
goto end;
}
list_append(&tl->textures, &tx->node);
}
*out = tl;
tl = NULL;
r = 0;
end:
tex_list_free(tl);
return r;
}
void tex_list_free(struct tex_list *tl) {
struct list_node *next;
struct list_node *pos;
struct tex_texture *tx;
if (tl == NULL) {
return;
}
for (pos = tl->textures.head; pos != NULL; pos = next) {
next = pos->next;
tx = containerof(pos, struct tex_texture, node);
tex_texture_free(tx);
}
free(tl);
}
const struct tex_texture *
tex_list_get_first_texture(const struct tex_list *tl) {
struct list_node *head;
assert(tl != NULL);
head = tl->textures.head;
if (head == NULL) {
return NULL;
}
return containerof(head, struct tex_texture, node);
}
static int tex_texture_read(struct tex_texture **out, const struct prop *p) {
struct tex_texture *tx;
struct tex_image *ti;
const struct prop *child;
const char *p_name;
const char *a_format;
bool got_size;
int r;
assert(out != NULL);
assert(p != NULL);
*out = NULL;
tx = NULL;
got_size = false;
p_name = prop_get_name(p);
if (!str_eq(p_name, tex_texture_prop_name)) {
r = -EBADMSG;
log_write("Expected prop name \"%s\", got \"%s\"", tex_texture_prop_name,
p_name);
goto end;
}
tx = calloc(1, sizeof(*tx));
if (tx == NULL) {
r = -ENOMEM;
goto end;
}
a_format = prop_get_attr(p, "format");
if (a_format == NULL) {
r = -EBADMSG;
log_write("No format attr on texture");
goto end;
}
r = str_dup(&tx->format, a_format);
if (r < 0) {
goto end;
}
for (child = prop_get_first_child_const(p); child != NULL;
child = prop_get_next_sibling_const(child)) {
if (str_eq(prop_get_name(child), tex_texture_size_prop_name)) {
r = tex_texture_read_size(&tx->size, child);
if (r < 0) {
goto end;
}
got_size = true;
} else {
r = tex_image_read(&ti, child);
if (r < 0) {
goto end;
}
list_append(&tx->images, &ti->node);
}
}
if (!got_size) {
r = -EBADMSG;
log_write("Texture has no \"%s\" node", tex_texture_size_prop_name);
goto end;
}
*out = tx;
tx = NULL;
r = 0;
end:
tex_texture_free(tx);
return r;
}
static int tex_texture_read_size(struct dim *dest, const struct prop *p) {
enum prop_type type;
struct const_iobuf src;
uint16_t u16;
int r;
assert(dest != NULL);
assert(p != NULL);
memset(dest, 0, sizeof(*dest));
type = prop_get_type(p);
if (type != PROP_2U16) {
log_write("Expected \"%s\" node type 2u16, got type %x",
tex_texture_size_prop_name, type);
return -EBADMSG;
}
prop_borrow_value(p, &src);
r = iobuf_read_be16(&src, &u16);
assert(r >= 0);
dest->width = u16;
r = iobuf_read_be16(&src, &u16);
assert(r >= 0);
dest->height = u16;
return 0;
}
static void tex_texture_free(struct tex_texture *tx) {
struct list_node *pos;
struct list_node *next;
struct tex_image *ti;
if (tx == NULL) {
return;
}
free(tx->format);
for (pos = tx->images.head; pos != NULL; pos = next) {
next = pos->next;
ti = containerof(pos, struct tex_image, node);
tex_image_free(ti);
}
free(tx);
}
const struct tex_texture *
tex_texture_get_next_sibling(const struct tex_texture *tx) {
struct list_node *next;
assert(tx != NULL);
next = tx->node.next;
if (next == NULL) {
return NULL;
}
return containerof(next, struct tex_texture, node);
}
const struct tex_image *
tex_texture_get_first_image(const struct tex_texture *tx) {
struct list_node *head;
assert(tx != NULL);
head = tx->images.head;
if (head == NULL) {
return NULL;
}
return containerof(head, struct tex_image, node);
}
static int tex_image_read(struct tex_image **out, const struct prop *p) {
struct tex_image *ti;
struct md5_hash md5;
const char *a_name;
const char *p_name;
int r;
assert(out != NULL);
assert(p != NULL);
*out = NULL;
ti = NULL;
p_name = prop_get_name(p);
if (!str_eq(p_name, tex_image_prop_name)) {
r = -EBADMSG;
log_write("Expected prop name \"%s\", got \"%s\"", tex_image_prop_name,
p_name);
}
ti = calloc(1, sizeof(*ti));
if (ti == NULL) {
r = -ENOMEM;
goto end;
}
a_name = prop_get_attr(p, "name");
if (a_name == NULL) {
r = -EBADMSG;
log_write("Prop has no \"name\" attr");
goto end;
}
r = str_dup(&ti->name, a_name);
if (r < 0) {
goto end;
}
r = md5_compute(&md5, a_name, strlen(a_name));
if (r < 0) {
goto end;
}
r = hex_encode_lc(&ti->name_md5, md5.b, sizeof(md5.b));
if (r < 0) {
goto end;
}
r = tex_image_read_rect(&ti->uvrect, p, "uvrect");
if (r < 0) {
goto end;
}
r = tex_image_read_rect(&ti->imgrect, p, "imgrect");
if (r < 0) {
goto end;
}
*out = ti;
ti = NULL;
r = 0;
end:
tex_image_free(ti);
return r;
}
static int tex_image_read_rect(struct rect *rect, const struct prop *p,
const char *child_name) {
const struct prop *child;
struct const_iobuf buf;
enum prop_type type;
uint16_t u16;
int r;
assert(rect != NULL);
assert(p != NULL);
assert(child_name != NULL);
memset(rect, 0, sizeof(*rect));
child = prop_search_child_const(p, child_name);
if (child == NULL) {
log_write("Child \"%s\" not found", child_name);
return -EBADMSG;
}
type = prop_get_type(child);
if (type != PROP_4U16) {
log_write("\"%s\": Expected PROP_4U16, got %#x", child_name, type);
return -EBADMSG;
}
prop_borrow_value(child, &buf);
r = iobuf_read_be16(&buf, &u16);
assert(r >= 0);
rect->p1.x = u16;
r = iobuf_read_be16(&buf, &u16);
assert(r >= 0);
rect->p2.x = u16;
r = iobuf_read_be16(&buf, &u16);
assert(r >= 0);
rect->p1.y = u16;
r = iobuf_read_be16(&buf, &u16);
assert(r >= 0);
rect->p2.y = u16;
if (rect->p1.x > rect->p2.x) {
log_write("\"%s\": p1.x > p2.x", child_name);
return -EBADMSG;
}
if (rect->p1.y > rect->p2.y) {
log_write("\"%s\": p1.y > p2.y", child_name);
return -EBADMSG;
}
return 0;
}
static void tex_image_free(struct tex_image *ti) {
if (ti == NULL) {
return;
}
free(ti->name);
free(ti->name_md5);
free(ti);
}
const struct tex_image *tex_image_get_next_sibling(const struct tex_image *ti) {
struct list_node *next;
assert(ti != NULL);
next = ti->node.next;
if (next == NULL) {
return NULL;
}
return containerof(next, struct tex_image, node);
}
int tex_image_read_pixels(const struct tex_image *ti,
struct const_iobuf *lz_pixels, struct picture **out) {
struct picture *p;
struct dim dim;
void *pixels;
size_t nbytes;
size_t expected_nbytes;
int r;
assert(ti != NULL);
assert(lz_pixels != NULL);
assert(out != NULL);
*out = NULL;
pixels = NULL;
p = NULL;
r = lz_file_read(lz_pixels, &pixels, &nbytes);
if (r < 0) {
goto end;
}
assert(ti->imgrect.p1.x <= ti->imgrect.p2.x);
assert(ti->imgrect.p1.y <= ti->imgrect.p2.y);
/* Texture dimensions have to be divided by 2 for some reason */
dim.width = (ti->imgrect.p2.x - ti->imgrect.p1.x) / 2;
dim.height = (ti->imgrect.p2.y - ti->imgrect.p1.y) / 2;
expected_nbytes = dim.width * dim.height * sizeof(pixel_t);
if (expected_nbytes != nbytes) {
r = -EBADMSG;
log_write("Expected %#x pixel bytes, decompressed to %#x",
(unsigned int)expected_nbytes, (unsigned int)nbytes);
goto end;
}
r = picture_alloc(&p, &dim);
if (r < 0) {
goto end;
}
memcpy(p->pixels, pixels, nbytes);
r = 0;
*out = p;
p = NULL;
end:
free(p);
free(pixels);
return r;
}

40
573file/tex.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include "573file/prop.h"
#include "util/iobuf.h"
#include "util/list.h"
#include "picture/picture.h"
struct tex_list {
struct list textures;
};
struct tex_texture {
struct list_node node;
char *format;
struct dim size;
struct list images;
};
struct tex_image {
struct list_node node;
char *name;
char *name_md5;
struct rect uvrect;
struct rect imgrect;
};
int tex_list_read(struct tex_list **out, const struct prop *p);
void tex_list_free(struct tex_list *tl);
const struct tex_texture *tex_list_get_first_texture(const struct tex_list *tl);
const struct tex_texture *
tex_texture_get_next_sibling(const struct tex_texture *tx);
const struct tex_image *
tex_texture_get_first_image(const struct tex_texture *tx);
const struct tex_image *tex_image_get_next_sibling(const struct tex_image *ti);
int tex_image_read_pixels(const struct tex_image *tx,
struct const_iobuf *lz_pixels, struct picture **out);