mirror of
https://github.com/djhackersdev/saltytools.git
synced 2026-03-21 17:54:11 -05:00
460 lines
7.9 KiB
C
460 lines
7.9 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "573file/prop-binary-reader.h"
|
|
#include "573file/prop-type.h"
|
|
#include "573file/prop.h"
|
|
|
|
#include "util/iobuf.h"
|
|
#include "util/log.h"
|
|
#include "util/macro.h"
|
|
|
|
#define ALIGN32(x) (((x) + 3) & ~3)
|
|
#define INVALID_OFFSET ((size_t)-1)
|
|
|
|
struct prop_binary_parser {
|
|
struct const_iobuf head;
|
|
struct const_iobuf body;
|
|
struct const_iobuf align_cave[2];
|
|
};
|
|
|
|
static const char prop_binary_name_chars[] =
|
|
"0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
|
|
|
|
static int prop_binary_read_node(struct prop_binary_parser *bp, uint8_t type,
|
|
struct prop **p);
|
|
int prop_binary_header_read_name(struct prop_binary_parser *bp, char **out);
|
|
static int prop_binary_read_attr(struct prop_binary_parser *bp, struct prop *p);
|
|
static int prop_binary_slice_value(struct prop_binary_parser *bp, uint8_t type,
|
|
struct const_iobuf *out);
|
|
static int prop_binary_body_slice_bytes(struct prop_binary_parser *bp,
|
|
size_t nbytes, struct const_iobuf *out);
|
|
static int prop_binary_body_slice_cave(struct prop_binary_parser *bp,
|
|
size_t nbytes, struct const_iobuf *out);
|
|
|
|
int prop_binary_parse(struct prop **out, const void *bytes, size_t nbytes) {
|
|
struct prop *p;
|
|
struct prop_binary_parser bp;
|
|
struct const_iobuf file;
|
|
uint32_t head_nbytes;
|
|
uint32_t body_nbytes;
|
|
uint8_t type;
|
|
int r;
|
|
|
|
assert(out != NULL);
|
|
assert(bytes != NULL);
|
|
|
|
*out = NULL;
|
|
p = NULL;
|
|
memset(&bp, 0, sizeof(bp));
|
|
|
|
file.bytes = bytes;
|
|
file.nbytes = nbytes;
|
|
file.pos = 4; /* Skip initial magic # */
|
|
|
|
r = iobuf_read_be32(&file, &head_nbytes);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
goto end;
|
|
}
|
|
|
|
r = iobuf_slice(&bp.head, &file, head_nbytes);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
goto end;
|
|
}
|
|
|
|
r = iobuf_align_read(&file, 4);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
goto end;
|
|
}
|
|
|
|
r = iobuf_read_be32(&file, &body_nbytes);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
goto end;
|
|
}
|
|
|
|
r = iobuf_slice(&bp.body, &file, body_nbytes);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
goto end;
|
|
}
|
|
|
|
r = iobuf_read_8(&bp.head, &type);
|
|
|
|
if (r < 0) {
|
|
log_write("Failed to read root node type code");
|
|
|
|
goto end;
|
|
}
|
|
|
|
if (type == 0xFF) {
|
|
log_write("Binary prop has no root node");
|
|
r = -EBADMSG;
|
|
|
|
goto end;
|
|
}
|
|
|
|
r = prop_binary_read_node(&bp, type, &p);
|
|
|
|
if (r < 0) {
|
|
goto end;
|
|
}
|
|
|
|
r = iobuf_read_8(&bp.head, &type);
|
|
|
|
if (r < 0) {
|
|
goto end;
|
|
}
|
|
|
|
if (type != 0xFF) {
|
|
log_write("Expected EOF marker in header, got %#x", type);
|
|
r = -EBADMSG;
|
|
|
|
goto end;
|
|
}
|
|
|
|
*out = p;
|
|
p = NULL;
|
|
r = 0;
|
|
|
|
end:
|
|
prop_free(p);
|
|
|
|
return r;
|
|
}
|
|
|
|
static int prop_binary_read_node(struct prop_binary_parser *bp, uint8_t type,
|
|
struct prop **out) {
|
|
struct const_iobuf value;
|
|
struct prop *child;
|
|
struct prop *p;
|
|
uint8_t child_type;
|
|
char *name;
|
|
int r;
|
|
|
|
assert(bp != NULL);
|
|
assert(out != NULL);
|
|
|
|
*out = NULL;
|
|
name = NULL;
|
|
p = NULL;
|
|
|
|
r = prop_binary_header_read_name(bp, &name);
|
|
|
|
if (r < 0) {
|
|
log_write("Failed to read name");
|
|
|
|
goto end;
|
|
}
|
|
|
|
if (!prop_type_is_valid(type)) {
|
|
log_write("\"%s\": Unsupported type code %#x", name, type);
|
|
r = -ENOTSUP;
|
|
|
|
goto end;
|
|
}
|
|
|
|
r = prop_binary_slice_value(bp, type, &value);
|
|
|
|
if (r < 0) {
|
|
log_write("\"%s\": Failed to read value of type %s", name,
|
|
prop_type_to_string(type));
|
|
|
|
goto end;
|
|
}
|
|
|
|
r = prop_alloc(&p, name, type, value.bytes, value.nbytes);
|
|
|
|
if (r < 0) {
|
|
goto end;
|
|
}
|
|
|
|
for (;;) {
|
|
r = iobuf_read_8(&bp->head, &child_type);
|
|
|
|
if (r < 0) {
|
|
log_write("\"%s\": Failed to read next child's type code", name);
|
|
|
|
goto end;
|
|
}
|
|
|
|
if (child_type == 0xFE) {
|
|
break;
|
|
} else if (child_type == PROP_ATTR) {
|
|
r = prop_binary_read_attr(bp, p);
|
|
|
|
if (r < 0) {
|
|
goto end;
|
|
}
|
|
} else {
|
|
r = prop_binary_read_node(bp, child_type, &child);
|
|
|
|
if (r < 0) {
|
|
goto end;
|
|
}
|
|
|
|
prop_append(p, child);
|
|
}
|
|
}
|
|
|
|
*out = p;
|
|
p = NULL;
|
|
|
|
end:
|
|
free(name);
|
|
prop_free(p);
|
|
|
|
return r;
|
|
}
|
|
|
|
int prop_binary_header_read_name(struct prop_binary_parser *bp, char **out) {
|
|
uint8_t nchars;
|
|
uint8_t x;
|
|
uint8_t y;
|
|
uint8_t z;
|
|
char *name;
|
|
int i;
|
|
int index;
|
|
int r;
|
|
|
|
*out = NULL;
|
|
name = NULL;
|
|
|
|
r = iobuf_read_8(&bp->head, &nchars);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
goto end;
|
|
}
|
|
|
|
name = malloc(nchars + 1);
|
|
|
|
if (name == NULL) {
|
|
r = -ENOMEM;
|
|
|
|
goto end;
|
|
}
|
|
|
|
x = 0;
|
|
y = 0;
|
|
z = 0;
|
|
|
|
for (i = 0; i < nchars; i++) {
|
|
switch (i % 4) {
|
|
case 0:
|
|
r = iobuf_read_8(&bp->head, &x);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
goto end;
|
|
}
|
|
|
|
index = (x >> 2) & 0x3F;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
r = iobuf_read_8(&bp->head, &y);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
goto end;
|
|
}
|
|
|
|
index = ((x & 0x03) << 4) | ((y >> 4) & 0x0F);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
r = iobuf_read_8(&bp->head, &z);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
goto end;
|
|
}
|
|
|
|
index = ((y & 0x0F) << 2) | ((z >> 6) & 0x03);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
index = z & 0x3F;
|
|
|
|
break;
|
|
}
|
|
|
|
assert(index < lengthof(prop_binary_name_chars));
|
|
|
|
name[i] = prop_binary_name_chars[index];
|
|
}
|
|
|
|
name[nchars] = '\0';
|
|
|
|
*out = name;
|
|
name = NULL;
|
|
r = 0;
|
|
|
|
end:
|
|
free(name);
|
|
|
|
return r;
|
|
}
|
|
|
|
static int prop_binary_read_attr(struct prop_binary_parser *bp,
|
|
struct prop *p) {
|
|
struct const_iobuf value;
|
|
char *name;
|
|
int r;
|
|
|
|
name = NULL;
|
|
r = prop_binary_header_read_name(bp, &name);
|
|
|
|
if (r < 0) {
|
|
goto end;
|
|
}
|
|
|
|
r = prop_binary_slice_value(bp, PROP_ATTR, &value);
|
|
|
|
if (r < 0) {
|
|
goto end;
|
|
}
|
|
|
|
if (value.nbytes == 0) {
|
|
log_write("Attr @%s has zero length", name);
|
|
r = -EBADMSG;
|
|
|
|
goto end;
|
|
}
|
|
|
|
if (value.bytes[value.nbytes - 1] != '\0') {
|
|
log_write("Attr @%s is not NUL terminated", name);
|
|
r = -EBADMSG;
|
|
|
|
goto end;
|
|
}
|
|
|
|
r = prop_set_attr(p, name, (const char *)value.bytes);
|
|
|
|
if (r < 0) {
|
|
goto end;
|
|
}
|
|
|
|
r = 0;
|
|
|
|
end:
|
|
free(name);
|
|
|
|
return r;
|
|
}
|
|
|
|
static int prop_binary_slice_value(struct prop_binary_parser *bp, uint8_t type,
|
|
struct const_iobuf *out) {
|
|
int orig_nbytes;
|
|
uint32_t nbytes;
|
|
bool is_variable;
|
|
int r;
|
|
|
|
assert(bp != NULL);
|
|
assert(out != NULL);
|
|
|
|
memset(out, 0, sizeof(*out));
|
|
|
|
r = iobuf_align_read(&bp->body, 4);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
return r;
|
|
}
|
|
|
|
orig_nbytes = prop_type_to_size(type);
|
|
|
|
if (orig_nbytes < 0 || prop_type_is_array(type)) {
|
|
is_variable = true;
|
|
r = iobuf_read_be32(&bp->body, &nbytes);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
return r;
|
|
}
|
|
} else {
|
|
is_variable = false;
|
|
nbytes = orig_nbytes;
|
|
}
|
|
|
|
if (is_variable || nbytes >= 4) {
|
|
return prop_binary_body_slice_bytes(bp, nbytes, out);
|
|
} else if (nbytes > 0) {
|
|
return prop_binary_body_slice_cave(bp, nbytes, out);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int prop_binary_body_slice_bytes(struct prop_binary_parser *bp,
|
|
size_t nbytes,
|
|
struct const_iobuf *out) {
|
|
int r;
|
|
|
|
r = iobuf_slice(out, &bp->body, nbytes);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int prop_binary_body_slice_cave(struct prop_binary_parser *bp,
|
|
size_t nbytes, struct const_iobuf *out) {
|
|
struct const_iobuf *cave;
|
|
int r;
|
|
|
|
assert(bp != NULL);
|
|
assert(nbytes == 1 || nbytes == 2);
|
|
assert(out != NULL);
|
|
|
|
cave = &bp->align_cave[nbytes - 1];
|
|
|
|
if (cave->pos >= cave->nbytes) {
|
|
r = iobuf_slice(cave, &bp->body, 4);
|
|
|
|
if (r < 0) {
|
|
log_error(r);
|
|
|
|
return r;
|
|
}
|
|
}
|
|
|
|
r = iobuf_slice(out, cave, nbytes);
|
|
|
|
assert(r >= 0);
|
|
|
|
return 0;
|
|
}
|