mirror of
https://github.com/djhackersdev/saltytools.git
synced 2026-09-08 02:25:28 -05:00
feat: initial ifsdump and xmldump
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.cache/
|
||||
.vscode/launch.json
|
||||
8
.vscode/extensions.json
vendored
Normal file
8
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"llvm-vs-code-extensions.vscode-clangd",
|
||||
"mesonbuild.mesonbuild",
|
||||
"webfreak.debug",
|
||||
"xaver.clang-format",
|
||||
]
|
||||
}
|
||||
18
.vscode/settings.json
vendored
Normal file
18
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"[c]": {
|
||||
"editor.defaultFormatter": "xaver.clang-format"
|
||||
},
|
||||
"[cpp]": {
|
||||
"editor.defaultFormatter": "xaver.clang-format"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "vscode.json-language-features"
|
||||
},
|
||||
"[meson]": {
|
||||
"editor.formatOnSave": false
|
||||
},
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true,
|
||||
"files.insertFinalNewline": true,
|
||||
"mesonbuild.buildFolder": "build",
|
||||
}
|
||||
356
573file/ifs.c
Normal file
356
573file/ifs.c
Normal file
@@ -0,0 +1,356 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "573file/ifs.h"
|
||||
#include "573file/prop-binary-reader.h"
|
||||
#include "573file/prop.h"
|
||||
|
||||
#include "util/fs.h"
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
#include "util/macro.h"
|
||||
#include "util/str.h"
|
||||
|
||||
enum ifs_stat_entry {
|
||||
IFS_STAT_ENTRY_OFFSET,
|
||||
IFS_STAT_ENTRY_NBYTES,
|
||||
IFS_STAT_ENTRY_TIMESTAMP,
|
||||
IFS_STAT_LENGTH_
|
||||
};
|
||||
|
||||
struct ifs {
|
||||
FILE *f;
|
||||
uint32_t body_start;
|
||||
struct prop *root;
|
||||
};
|
||||
|
||||
static int ifs_dirent_match(const struct prop *dirent, const char *path);
|
||||
|
||||
int ifs_open(struct ifs **out, const char *path) {
|
||||
struct ifs *ifs;
|
||||
struct iobuf dest;
|
||||
struct const_iobuf src;
|
||||
uint8_t header_bytes[0x24];
|
||||
uint32_t header_words[9];
|
||||
void *pp_bytes;
|
||||
size_t pp_nbytes;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
assert(out != NULL);
|
||||
assert(path != NULL);
|
||||
|
||||
*out = NULL;
|
||||
pp_bytes = NULL;
|
||||
|
||||
ifs = calloc(1, sizeof(*ifs));
|
||||
|
||||
if (ifs == NULL) {
|
||||
r = -ENOMEM;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = fs_open(&ifs->f, path, "rb");
|
||||
|
||||
if (r < 0) {
|
||||
log_write("%s: Error opening file: %s (%i)", path, strerror(-r), r);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
dest.bytes = header_bytes;
|
||||
dest.nbytes = sizeof(header_bytes);
|
||||
dest.pos = 0;
|
||||
|
||||
r = fs_read(ifs->f, &dest);
|
||||
|
||||
if (r < 0) {
|
||||
log_write("%s: Error reading header: %s (%i)", path, strerror(-r), r);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
src.bytes = header_bytes;
|
||||
src.nbytes = sizeof(header_bytes);
|
||||
src.pos = 0;
|
||||
|
||||
for (i = 0; i < lengthof(header_words); i++) {
|
||||
r = iobuf_read_be32(&src, &header_words[i]);
|
||||
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
ifs->body_start = header_words[4];
|
||||
|
||||
pp_nbytes = ifs->body_start - sizeof(header_bytes);
|
||||
pp_bytes = malloc(pp_nbytes);
|
||||
|
||||
if (pp_bytes == NULL) {
|
||||
r = -ENOMEM;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
dest.bytes = pp_bytes;
|
||||
dest.nbytes = pp_nbytes;
|
||||
dest.pos = 0;
|
||||
|
||||
r = fs_read(ifs->f, &dest);
|
||||
|
||||
if (r < 0) {
|
||||
log_write("%s: Error reading TOC: %s (%i)", path, strerror(-r), r);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = prop_binary_parse(&ifs->root, pp_bytes, pp_nbytes);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!ifs_dirent_is_dir(ifs->root)) {
|
||||
log_write("%s: Root dirent is not a directory", path);
|
||||
r = -EBADMSG;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
*out = ifs;
|
||||
ifs = NULL;
|
||||
r = 0;
|
||||
|
||||
end:
|
||||
free(pp_bytes);
|
||||
ifs_close(ifs);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void ifs_close(struct ifs *ifs) {
|
||||
if (ifs == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ifs->f != NULL) {
|
||||
fclose(ifs->f);
|
||||
}
|
||||
|
||||
prop_free(ifs->root);
|
||||
free(ifs);
|
||||
}
|
||||
|
||||
const struct prop *ifs_get_root(struct ifs *ifs) {
|
||||
assert(ifs != NULL);
|
||||
|
||||
return ifs->root;
|
||||
}
|
||||
|
||||
int ifs_read_file(struct ifs *ifs, const struct prop *dirent, void *bytes,
|
||||
size_t *nbytes_out) {
|
||||
struct iobuf dest;
|
||||
struct const_iobuf stat_buf;
|
||||
uint32_t stat[IFS_STAT_LENGTH_];
|
||||
size_t offset;
|
||||
size_t nbytes;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
assert(ifs != NULL);
|
||||
assert(dirent != NULL);
|
||||
assert(nbytes_out != NULL);
|
||||
|
||||
if (prop_get_type(dirent) != PROP_3S32) {
|
||||
log_write("%s: Expected dirent to be PROP_3S32", prop_get_name(dirent));
|
||||
r = -EINVAL;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
prop_borrow_value(dirent, &stat_buf);
|
||||
|
||||
for (i = 0; i < lengthof(stat); i++) {
|
||||
r = iobuf_read_be32(&stat_buf, &stat[i]);
|
||||
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
nbytes = stat[IFS_STAT_ENTRY_NBYTES];
|
||||
|
||||
if (bytes != NULL) {
|
||||
if (*nbytes_out < nbytes) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
offset = ifs->body_start + stat[IFS_STAT_ENTRY_OFFSET];
|
||||
r = fs_seek_to(ifs->f, offset);
|
||||
|
||||
if (r < 0) {
|
||||
log_write("%s: IFS seek failed (offset=%#lx): %s (%i)",
|
||||
prop_get_name(dirent), offset, strerror(-r), r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
dest.bytes = bytes;
|
||||
dest.nbytes = nbytes;
|
||||
dest.pos = 0;
|
||||
|
||||
r = fs_read(ifs->f, &dest);
|
||||
|
||||
if (r < 0) {
|
||||
log_write("%s: IFS read failed (nbytes=%#lx): %s (%i)",
|
||||
prop_get_name(dirent), nbytes, strerror(-r), r);
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
*nbytes_out = nbytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ifs_dirent_lookup(const struct prop *dirent, const char *path,
|
||||
const struct prop **out) {
|
||||
const struct prop *pos;
|
||||
int r;
|
||||
|
||||
assert(dirent != NULL);
|
||||
assert(path != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
for (pos = ifs_dirent_get_first_child(dirent); pos != NULL;
|
||||
pos = ifs_dirent_get_next_sibling(pos)) {
|
||||
r = ifs_dirent_match(pos, path);
|
||||
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (r > 0) {
|
||||
*out = pos;
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ifs_dirent_match(const struct prop *dirent, const char *path) {
|
||||
char *str;
|
||||
int r;
|
||||
|
||||
assert(dirent != NULL);
|
||||
assert(path != NULL);
|
||||
|
||||
str = NULL;
|
||||
r = ifs_dirent_get_name(dirent, &str);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = str_eq(str, path) ? 1 : 0;
|
||||
|
||||
end:
|
||||
free(str);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
const struct prop *ifs_dirent_get_first_child(const struct prop *dirent) {
|
||||
const struct prop *pos;
|
||||
|
||||
assert(dirent != NULL);
|
||||
|
||||
pos = prop_get_first_child_const(dirent);
|
||||
|
||||
if (pos != NULL && str_eq(prop_get_name(pos), "_info_")) {
|
||||
pos = prop_get_next_sibling_const(pos);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
const struct prop *ifs_dirent_get_next_sibling(const struct prop *dirent) {
|
||||
assert(dirent != NULL);
|
||||
|
||||
return prop_get_next_sibling_const(dirent);
|
||||
}
|
||||
|
||||
int ifs_dirent_get_name(const struct prop *dirent, char **out) {
|
||||
bool escape;
|
||||
uint32_t i;
|
||||
uint32_t j;
|
||||
const char *raw;
|
||||
char *str;
|
||||
|
||||
assert(dirent != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
raw = prop_get_name(dirent);
|
||||
escape = false;
|
||||
j = 0;
|
||||
|
||||
for (i = 0; raw[i] != '\0'; i++) {
|
||||
if (escape) {
|
||||
escape = false;
|
||||
j++;
|
||||
} else if (raw[i] == '_') {
|
||||
escape = true;
|
||||
} else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
str = malloc(j + 1); /* Count terminating NUL */
|
||||
|
||||
if (str == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
escape = false;
|
||||
j = 0;
|
||||
|
||||
for (i = 0; raw[i] != '\0'; i++) {
|
||||
if (escape) {
|
||||
if (raw[i] == 'E') {
|
||||
str[j++] = '.';
|
||||
} else {
|
||||
/* Probably some other escapes I don't know about ... */
|
||||
str[j++] = raw[i];
|
||||
}
|
||||
|
||||
escape = false;
|
||||
} else if (raw[i] == '_') {
|
||||
escape = true;
|
||||
} else {
|
||||
str[j++] = raw[i];
|
||||
}
|
||||
}
|
||||
|
||||
str[j] = '\0';
|
||||
*out = str;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ifs_dirent_is_dir(const struct prop *dirent) {
|
||||
enum prop_type type;
|
||||
|
||||
assert(dirent != NULL);
|
||||
|
||||
type = prop_get_type(dirent);
|
||||
|
||||
return type == PROP_VOID || type == PROP_S32;
|
||||
}
|
||||
20
573file/ifs.h
Normal file
20
573file/ifs.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "573file/prop.h"
|
||||
|
||||
struct ifs;
|
||||
|
||||
int ifs_open(struct ifs **ifs, const char *path);
|
||||
void ifs_close(struct ifs *ifs);
|
||||
const struct prop *ifs_get_root(struct ifs *ifs);
|
||||
int ifs_read_file(struct ifs *ifs, const struct prop *dirent, void *bytes,
|
||||
size_t *nbytes);
|
||||
|
||||
int ifs_dirent_lookup(const struct prop *dirent, const char *path,
|
||||
const struct prop **out);
|
||||
const struct prop *ifs_dirent_get_first_child(const struct prop *dirent);
|
||||
const struct prop *ifs_dirent_get_next_sibling(const struct prop *dirent);
|
||||
int ifs_dirent_get_name(const struct prop *dirent, char **out);
|
||||
bool ifs_dirent_is_dir(const struct prop *dirent);
|
||||
17
573file/meson.build
Normal file
17
573file/meson.build
Normal file
@@ -0,0 +1,17 @@
|
||||
_573file_lib = static_library(
|
||||
'573file',
|
||||
c_pch: '../precompiled.h',
|
||||
include_directories: [inc],
|
||||
sources: [
|
||||
'ifs.c',
|
||||
'ifs.h',
|
||||
'prop-binary-reader.c',
|
||||
'prop-binary-reader.h',
|
||||
'prop-type.c',
|
||||
'prop-type.h',
|
||||
'prop-xml-writer.c',
|
||||
'prop-xml-writer.h',
|
||||
'prop.c',
|
||||
'prop.h',
|
||||
]
|
||||
)
|
||||
459
573file/prop-binary-reader.c
Normal file
459
573file/prop-binary-reader.c
Normal file
@@ -0,0 +1,459 @@
|
||||
#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) {
|
||||
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;
|
||||
}
|
||||
7
573file/prop-binary-reader.h
Normal file
7
573file/prop-binary-reader.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "573file/prop.h"
|
||||
|
||||
int prop_binary_parse(struct prop **p, const void *bytes, size_t nbytes);
|
||||
43
573file/prop-type.c
Normal file
43
573file/prop-type.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "573file/prop-type.h"
|
||||
|
||||
#include "util/macro.h"
|
||||
|
||||
static const char *const prop_type_names[64] = {
|
||||
[PROP_VOID] = "void", [PROP_S8] = "s8", [PROP_U8] = "u8",
|
||||
[PROP_S16] = "s16", [PROP_U16] = "u16", [PROP_S32] = "s32",
|
||||
[PROP_U32] = "u32", [PROP_S64] = "s64", [PROP_U64] = "u64",
|
||||
[PROP_BIN] = "bin", [PROP_STR] = "str", [PROP_IP4] = "ip4",
|
||||
[PROP_TIME] = "time", [PROP_2U16] = "2u16", [PROP_3S32] = "3s32",
|
||||
[PROP_4U16] = "4u16", [PROP_BOOL] = "bool",
|
||||
};
|
||||
|
||||
static const int prop_type_sizes[64] = {
|
||||
[PROP_VOID] = 0, [PROP_S8] = 1, [PROP_U8] = 1, [PROP_S16] = 2,
|
||||
[PROP_U16] = 2, [PROP_S32] = 4, [PROP_U32] = 4, [PROP_S64] = 8,
|
||||
[PROP_U64] = 8, [PROP_BIN] = -1, [PROP_STR] = -1, [PROP_IP4] = 4,
|
||||
[PROP_TIME] = 4, [PROP_2U16] = 4, [PROP_3S32] = 12, [PROP_4U16] = 8,
|
||||
[PROP_ATTR] = -1, [PROP_BOOL] = 1,
|
||||
};
|
||||
|
||||
bool prop_type_is_valid(enum prop_type type) {
|
||||
if (type < 0 || type >= lengthof(prop_type_names)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prop_type_names[type] != NULL;
|
||||
}
|
||||
|
||||
const char *prop_type_to_string(enum prop_type type) {
|
||||
assert(prop_type_is_valid(type));
|
||||
|
||||
return prop_type_names[type];
|
||||
}
|
||||
|
||||
int prop_type_to_size(enum prop_type type) {
|
||||
assert(type == PROP_ATTR /* hack */ || prop_type_is_valid(type));
|
||||
|
||||
return prop_type_sizes[type];
|
||||
}
|
||||
28
573file/prop-type.h
Normal file
28
573file/prop-type.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
enum prop_type {
|
||||
PROP_INVALID_TYPE = 0x00,
|
||||
PROP_VOID = 0x01,
|
||||
PROP_S8 = 0x02,
|
||||
PROP_U8 = 0x03,
|
||||
PROP_S16 = 0x04,
|
||||
PROP_U16 = 0x05,
|
||||
PROP_S32 = 0x06,
|
||||
PROP_U32 = 0x07,
|
||||
PROP_S64 = 0x08,
|
||||
PROP_U64 = 0x09,
|
||||
PROP_BIN = 0x0A,
|
||||
PROP_STR = 0x0B,
|
||||
PROP_IP4 = 0x0C,
|
||||
PROP_TIME = 0x0D,
|
||||
PROP_2U16 = 0x13,
|
||||
PROP_3S32 = 0x1E,
|
||||
PROP_4U16 = 0x27,
|
||||
PROP_ATTR = 0x2E, /* Pseudo-type */
|
||||
PROP_BOOL = 0x34,
|
||||
PROP_ARRAY_FLAG = 0x40
|
||||
};
|
||||
|
||||
bool prop_type_is_valid(enum prop_type type);
|
||||
const char *prop_type_to_string(enum prop_type type);
|
||||
int prop_type_to_size(enum prop_type type);
|
||||
617
573file/prop-xml-writer.c
Normal file
617
573file/prop-xml-writer.c
Normal file
@@ -0,0 +1,617 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "573file/prop-xml-writer.h"
|
||||
#include "573file/prop.h"
|
||||
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
enum prop_xml_escape {
|
||||
PROP_XML_ESCAPE_ATTR,
|
||||
PROP_XML_ESCAPE_TEXT,
|
||||
};
|
||||
|
||||
static void prop_xml_write_attr_list(struct strbuf *dest, const struct prop *p);
|
||||
static void prop_xml_write_children(struct strbuf *dest, const struct prop *p,
|
||||
unsigned int indent);
|
||||
static void prop_xml_write_escaped_string(struct strbuf *dest, const char *str,
|
||||
enum prop_xml_escape ctx);
|
||||
static void prop_xml_write_indent(struct strbuf *dest, unsigned int indent);
|
||||
static void prop_xml_write_node(struct strbuf *dest, const struct prop *p,
|
||||
unsigned int indent);
|
||||
static void prop_xml_write_nonvoid(struct strbuf *dest, const struct prop *p,
|
||||
unsigned int indent);
|
||||
static void prop_xml_write_void(struct strbuf *dest, const struct prop *p,
|
||||
unsigned int indent);
|
||||
static void prop_xml_write_text(struct strbuf *dest, const struct prop *p,
|
||||
enum prop_xml_escape ctx);
|
||||
static void prop_xml_write_text_s8(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_s16(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_s32(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_s64(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_u8(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_u16(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_u32(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_u64(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_bin(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_str(struct strbuf *dest,
|
||||
struct const_iobuf *src,
|
||||
enum prop_xml_escape ctx);
|
||||
static void prop_xml_write_text_ip4(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_bool(struct strbuf *dest,
|
||||
struct const_iobuf *src);
|
||||
static void prop_xml_write_text_s32_tuple(struct strbuf *dest,
|
||||
struct const_iobuf *src,
|
||||
size_t count);
|
||||
static void prop_xml_write_text_u16_tuple(struct strbuf *dest,
|
||||
struct const_iobuf *src,
|
||||
size_t count);
|
||||
|
||||
int prop_xml_write(const struct prop *p, char **out) {
|
||||
struct strbuf buf;
|
||||
char *chars;
|
||||
|
||||
assert(p != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
buf.chars = NULL;
|
||||
buf.nchars = 0;
|
||||
buf.pos = 0;
|
||||
|
||||
prop_xml_write_node(&buf, p, 0);
|
||||
chars = malloc(buf.pos + 1);
|
||||
|
||||
if (chars == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
buf.chars = chars;
|
||||
buf.nchars = buf.pos + 1;
|
||||
buf.pos = 0;
|
||||
|
||||
prop_xml_write_node(&buf, p, 0);
|
||||
|
||||
assert(buf.pos + 1 == buf.nchars);
|
||||
|
||||
*out = chars;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prop_xml_write_node(struct strbuf *dest, const struct prop *p,
|
||||
unsigned int indent) {
|
||||
enum prop_type type;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(p != NULL);
|
||||
|
||||
type = prop_get_type(p);
|
||||
|
||||
if (type == PROP_VOID) {
|
||||
prop_xml_write_void(dest, p, indent);
|
||||
} else {
|
||||
prop_xml_write_nonvoid(dest, p, indent);
|
||||
}
|
||||
}
|
||||
|
||||
static void prop_xml_write_void(struct strbuf *dest, const struct prop *p,
|
||||
unsigned int indent) {
|
||||
const struct prop *first_child;
|
||||
const char *name;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(p != NULL);
|
||||
|
||||
name = prop_get_name(p);
|
||||
|
||||
prop_xml_write_indent(dest, indent);
|
||||
strbuf_printf(dest, "<%s", name);
|
||||
prop_xml_write_attr_list(dest, p);
|
||||
|
||||
first_child = prop_get_first_child_const(p);
|
||||
|
||||
if (first_child != NULL) {
|
||||
strbuf_puts(dest, ">\n");
|
||||
prop_xml_write_children(dest, p, indent + 1);
|
||||
prop_xml_write_indent(dest, indent);
|
||||
strbuf_printf(dest, "</%s>\n", name);
|
||||
} else {
|
||||
strbuf_puts(dest, "/>\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* AVS doesn't understand this __value attribute here, this is my own
|
||||
invention, but generally we don't see any property pages with mixed content
|
||||
nodes being stored in XML format to begin with. */
|
||||
|
||||
static void prop_xml_write_nonvoid(struct strbuf *dest, const struct prop *p,
|
||||
unsigned int indent) {
|
||||
const struct prop *first_child;
|
||||
const char *name;
|
||||
const char *type_str;
|
||||
enum prop_type type;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(p != NULL);
|
||||
|
||||
name = prop_get_name(p);
|
||||
type = prop_get_type(p);
|
||||
type_str = prop_type_to_string(type);
|
||||
|
||||
prop_xml_write_indent(dest, indent);
|
||||
strbuf_printf(dest, "<%s __type=\"%s\"", name, type_str);
|
||||
|
||||
first_child = prop_get_first_child_const(p);
|
||||
|
||||
if (first_child != NULL) {
|
||||
strbuf_puts(dest, " __value=\"");
|
||||
prop_xml_write_text(dest, p, PROP_XML_ESCAPE_ATTR);
|
||||
strbuf_putc(dest, '"');
|
||||
|
||||
prop_xml_write_attr_list(dest, p);
|
||||
strbuf_puts(dest, ">\n");
|
||||
|
||||
prop_xml_write_children(dest, p, indent + 1);
|
||||
|
||||
prop_xml_write_indent(dest, indent);
|
||||
strbuf_printf(dest, "</%s>\n", name);
|
||||
} else {
|
||||
prop_xml_write_attr_list(dest, p);
|
||||
strbuf_putc(dest, '>');
|
||||
|
||||
prop_xml_write_text(dest, p, PROP_XML_ESCAPE_TEXT);
|
||||
|
||||
strbuf_printf(dest, "</%s>\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
static void prop_xml_write_children(struct strbuf *dest, const struct prop *p,
|
||||
unsigned int indent) {
|
||||
const struct prop *child;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(p != NULL);
|
||||
|
||||
for (child = prop_get_first_child_const(p); child != NULL;
|
||||
child = prop_get_next_sibling_const(child)) {
|
||||
prop_xml_write_node(dest, child, indent);
|
||||
}
|
||||
}
|
||||
|
||||
static void prop_xml_write_attr_list(struct strbuf *dest,
|
||||
const struct prop *p) {
|
||||
const struct attr *a;
|
||||
const char *attr_key;
|
||||
const char *attr_val;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(p != NULL);
|
||||
|
||||
for (a = prop_get_first_attr(p); a != NULL; a = attr_get_next_sibling(a)) {
|
||||
attr_key = attr_get_key(a);
|
||||
attr_val = attr_get_val(a);
|
||||
|
||||
strbuf_printf(dest, " %s=\"", attr_key);
|
||||
prop_xml_write_escaped_string(dest, attr_val, PROP_XML_ESCAPE_ATTR);
|
||||
strbuf_putc(dest, '"');
|
||||
}
|
||||
}
|
||||
|
||||
static void prop_xml_write_escaped_string(struct strbuf *dest, const char *str,
|
||||
enum prop_xml_escape ctx) {
|
||||
const char *pos;
|
||||
char c;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(str != NULL);
|
||||
|
||||
for (pos = str; *pos != '\0'; pos++) {
|
||||
c = *pos;
|
||||
|
||||
switch (c) {
|
||||
case '<':
|
||||
strbuf_puts(dest, "<");
|
||||
|
||||
break;
|
||||
|
||||
case '>':
|
||||
strbuf_puts(dest, ">");
|
||||
|
||||
break;
|
||||
|
||||
case '&':
|
||||
strbuf_puts(dest, "&");
|
||||
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
if (ctx == PROP_XML_ESCAPE_ATTR) {
|
||||
strbuf_puts(dest, "'");
|
||||
} else {
|
||||
strbuf_putc(dest, c);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '"':
|
||||
if (ctx == PROP_XML_ESCAPE_ATTR) {
|
||||
strbuf_puts(dest, """);
|
||||
} else {
|
||||
strbuf_putc(dest, c);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
strbuf_putc(dest, c);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prop_xml_write_indent(struct strbuf *dest, unsigned int indent) {
|
||||
unsigned int i;
|
||||
|
||||
assert(dest != NULL);
|
||||
|
||||
for (i = 0; i < indent; i++) {
|
||||
strbuf_puts(dest, " ");
|
||||
}
|
||||
}
|
||||
|
||||
static void prop_xml_write_text(struct strbuf *dest, const struct prop *p,
|
||||
enum prop_xml_escape ctx) {
|
||||
struct const_iobuf src;
|
||||
enum prop_type type;
|
||||
|
||||
type = prop_get_type(p);
|
||||
prop_borrow_value(p, &src);
|
||||
|
||||
switch (type) {
|
||||
case PROP_S8:
|
||||
prop_xml_write_text_s8(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_S16:
|
||||
prop_xml_write_text_s16(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_S32:
|
||||
prop_xml_write_text_s32(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_S64:
|
||||
prop_xml_write_text_s64(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_U8:
|
||||
prop_xml_write_text_u8(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_U16:
|
||||
prop_xml_write_text_u16(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_U32:
|
||||
prop_xml_write_text_u32(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_U64:
|
||||
prop_xml_write_text_u64(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_BIN:
|
||||
prop_xml_write_text_bin(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_STR:
|
||||
prop_xml_write_text_str(dest, &src, ctx);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_IP4:
|
||||
prop_xml_write_text_ip4(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_TIME:
|
||||
/* Reuse the u32 writer for timestamps */
|
||||
prop_xml_write_text_u32(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_2U16:
|
||||
prop_xml_write_text_u16_tuple(dest, &src, 2);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_3S32:
|
||||
prop_xml_write_text_s32_tuple(dest, &src, 3);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_4U16:
|
||||
prop_xml_write_text_u16_tuple(dest, &src, 4);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_BOOL:
|
||||
prop_xml_write_text_bool(dest, &src);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_write("Unsupported type: %#x", type);
|
||||
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_s8(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
uint8_t value;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
r = iobuf_read_8(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%i", (int8_t)value);
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_s16(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
uint16_t value;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
r = iobuf_read_be16(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%i", (int16_t)value);
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_s32(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
uint32_t value;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
r = iobuf_read_be32(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%i", (int32_t)value);
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_s64(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
uint64_t value;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
r = iobuf_read_be64(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%li", (int64_t)value);
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_u8(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
uint8_t value;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
r = iobuf_read_8(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%i", value);
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_u16(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
uint16_t value;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
r = iobuf_read_be16(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%u", value);
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_u32(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
uint32_t value;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
r = iobuf_read_be32(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%u", value);
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_u64(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
uint64_t value;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
r = iobuf_read_be64(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%lu", value);
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_bin(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
uint8_t byte;
|
||||
uint8_t nibble;
|
||||
uint8_t nibbles[2];
|
||||
size_t i;
|
||||
size_t j;
|
||||
char c;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
for (i = src->pos; i < src->nbytes; i++) {
|
||||
byte = src->bytes[i];
|
||||
nibbles[0] = byte >> 4;
|
||||
nibbles[1] = byte & 0x0f;
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
nibble = nibbles[j];
|
||||
|
||||
if (nibble < 10) {
|
||||
c = '0' + nibble;
|
||||
} else {
|
||||
c = 'a' + (nibble - 10);
|
||||
}
|
||||
|
||||
strbuf_putc(dest, c);
|
||||
}
|
||||
}
|
||||
|
||||
src->pos = i;
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_str(struct strbuf *dest,
|
||||
struct const_iobuf *src,
|
||||
enum prop_xml_escape ctx) {
|
||||
const char *chars;
|
||||
size_t nchars;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
assert(src->pos <= src->nbytes);
|
||||
|
||||
chars = (const char *)&src->bytes[src->pos];
|
||||
nchars = src->nbytes - src->pos;
|
||||
|
||||
assert(chars[nchars - 1] == '\0');
|
||||
|
||||
prop_xml_write_escaped_string(dest, chars, ctx);
|
||||
src->pos += nchars;
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_ip4(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
const uint8_t *b;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
assert(src->pos + 4 <= src->nbytes);
|
||||
|
||||
b = src->bytes + src->pos;
|
||||
strbuf_printf(dest, "%i.%i.%i.%i", b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_bool(struct strbuf *dest,
|
||||
struct const_iobuf *src) {
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
assert(src->pos < src->nbytes);
|
||||
|
||||
if (src->bytes[src->pos]) {
|
||||
strbuf_putc(dest, '1');
|
||||
} else {
|
||||
strbuf_putc(dest, '0');
|
||||
}
|
||||
}
|
||||
|
||||
/* I don't know how tuples are actually serialized to XML, the real format
|
||||
might be different. */
|
||||
|
||||
static void prop_xml_write_text_s32_tuple(struct strbuf *dest,
|
||||
struct const_iobuf *src,
|
||||
size_t count) {
|
||||
uint32_t value;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i > 0) {
|
||||
strbuf_putc(dest, ',');
|
||||
}
|
||||
|
||||
r = iobuf_read_be32(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%i", (int32_t)value);
|
||||
}
|
||||
}
|
||||
|
||||
static void prop_xml_write_text_u16_tuple(struct strbuf *dest,
|
||||
struct const_iobuf *src,
|
||||
size_t count) {
|
||||
uint16_t value;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i > 0) {
|
||||
strbuf_putc(dest, ',');
|
||||
}
|
||||
|
||||
r = iobuf_read_be16(src, &value);
|
||||
|
||||
assert(r >= 0);
|
||||
|
||||
strbuf_printf(dest, "%u", value);
|
||||
}
|
||||
}
|
||||
5
573file/prop-xml-writer.h
Normal file
5
573file/prop-xml-writer.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "573file/prop.h"
|
||||
|
||||
int prop_xml_write(const struct prop *p, char **out);
|
||||
385
573file/prop.c
Normal file
385
573file/prop.c
Normal file
@@ -0,0 +1,385 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "573file/prop-type.h"
|
||||
#include "573file/prop.h"
|
||||
|
||||
#include "util/list.h"
|
||||
#include "util/log.h"
|
||||
#include "util/macro.h"
|
||||
#include "util/str.h"
|
||||
|
||||
struct attr {
|
||||
struct list_node node;
|
||||
char *key;
|
||||
char *val;
|
||||
};
|
||||
|
||||
struct prop {
|
||||
struct list_node node;
|
||||
struct prop *parent;
|
||||
struct list attrs;
|
||||
struct list children;
|
||||
char *name;
|
||||
uint32_t nbytes;
|
||||
enum prop_type type;
|
||||
uint8_t bytes[];
|
||||
};
|
||||
|
||||
static int attr_set(struct attr *a, const char *val);
|
||||
|
||||
static int prop_validate(const char *name, enum prop_type type,
|
||||
const void *bytes, uint32_t nbytes);
|
||||
|
||||
static int prop_validate(const char *name, enum prop_type type,
|
||||
const void *bytes, uint32_t nbytes) {
|
||||
const char *chars;
|
||||
int expected_nbytes;
|
||||
|
||||
if (type & PROP_ARRAY_FLAG) {
|
||||
log_write("\"%s\": Arrays are not currently supported", name);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!prop_type_is_valid(type)) {
|
||||
log_write("\"%s\": Unsupported type code %#x", name, type);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
expected_nbytes = prop_type_to_size(type);
|
||||
|
||||
if (expected_nbytes >= 0 && nbytes != expected_nbytes) {
|
||||
log_write("\"%s\": Incorrect size %#x for type %s (expected %#x)", name,
|
||||
nbytes, prop_type_to_string(type), expected_nbytes);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (type == PROP_STR) {
|
||||
if (nbytes == 0) {
|
||||
log_write("\"%s\": String node has zero length", name);
|
||||
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
chars = bytes;
|
||||
|
||||
if (chars[nbytes - 1] != '\0') {
|
||||
log_write("\"%s\": String node is not NUL terminated", name);
|
||||
|
||||
return -EBADMSG;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prop_alloc(struct prop **out, const char *name, enum prop_type type,
|
||||
const void *bytes, uint32_t nbytes) {
|
||||
struct prop *p;
|
||||
int r;
|
||||
|
||||
assert(out != NULL);
|
||||
assert(name != NULL);
|
||||
assert(bytes != NULL || nbytes == 0);
|
||||
|
||||
*out = NULL;
|
||||
p = NULL;
|
||||
|
||||
r = prop_validate(name, type, bytes, nbytes);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
p = calloc(1, sizeof(*p) + nbytes);
|
||||
|
||||
if (p == NULL) {
|
||||
r = -ENOMEM;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
p->name = strdup(name);
|
||||
|
||||
if (p->name == NULL) {
|
||||
r = -ENOMEM;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
p->type = type;
|
||||
p->nbytes = nbytes;
|
||||
|
||||
memcpy(p->bytes, bytes, nbytes);
|
||||
|
||||
*out = p;
|
||||
p = NULL;
|
||||
r = 0;
|
||||
|
||||
end:
|
||||
prop_free(p);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void prop_free(struct prop *p) {
|
||||
struct list_node *pos;
|
||||
struct list_node *next;
|
||||
struct attr *attr;
|
||||
struct prop *child;
|
||||
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (pos = p->children.head; pos != NULL; pos = next) {
|
||||
next = pos->next;
|
||||
child = containerof(pos, struct prop, node);
|
||||
|
||||
prop_free(child);
|
||||
}
|
||||
|
||||
for (pos = p->attrs.head; pos != NULL; pos = next) {
|
||||
next = pos->next;
|
||||
attr = containerof(pos, struct attr, node);
|
||||
|
||||
free(attr->key);
|
||||
free(attr->val);
|
||||
free(attr);
|
||||
}
|
||||
|
||||
free(p->name);
|
||||
free(p);
|
||||
}
|
||||
|
||||
void prop_append(struct prop *p, struct prop *child) {
|
||||
assert(p != NULL);
|
||||
assert(child != NULL);
|
||||
assert(child->parent == NULL);
|
||||
|
||||
list_append(&p->children, &child->node);
|
||||
child->parent = p;
|
||||
}
|
||||
|
||||
void prop_borrow_value(const struct prop *p, struct const_iobuf *out) {
|
||||
assert(p != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
out->bytes = p->bytes;
|
||||
out->nbytes = p->nbytes;
|
||||
out->pos = 0;
|
||||
}
|
||||
|
||||
const char *prop_get_attr(const struct prop *p, const char *key) {
|
||||
struct list_node *pos;
|
||||
struct attr *a;
|
||||
|
||||
assert(p != NULL);
|
||||
assert(key != NULL);
|
||||
|
||||
for (pos = p->attrs.head; pos != NULL; pos = pos->next) {
|
||||
a = containerof(pos, struct attr, node);
|
||||
|
||||
if (str_eq(key, a->key)) {
|
||||
return a->val;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct attr *prop_get_first_attr(const struct prop *p) {
|
||||
struct list_node *attr;
|
||||
|
||||
assert(p != NULL);
|
||||
|
||||
attr = p->attrs.head;
|
||||
|
||||
if (attr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return containerof(attr, struct attr, node);
|
||||
}
|
||||
|
||||
struct prop *prop_get_first_child(struct prop *p) {
|
||||
struct list_node *child;
|
||||
|
||||
assert(p != NULL);
|
||||
|
||||
child = p->children.head;
|
||||
|
||||
if (child == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return containerof(child, struct prop, node);
|
||||
}
|
||||
|
||||
const struct prop *prop_get_first_child_const(const struct prop *p) {
|
||||
const struct list_node *child;
|
||||
|
||||
assert(p != NULL);
|
||||
|
||||
child = p->children.head;
|
||||
|
||||
if (child == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return containerof(child, struct prop, node);
|
||||
}
|
||||
|
||||
const char *prop_get_name(const struct prop *p) {
|
||||
assert(p != NULL);
|
||||
|
||||
return p->name;
|
||||
}
|
||||
|
||||
struct prop *prop_get_next_sibling(struct prop *p) {
|
||||
assert(p != NULL);
|
||||
|
||||
return containerof(p->node.next, struct prop, node);
|
||||
}
|
||||
|
||||
const struct prop *prop_get_next_sibling_const(const struct prop *p) {
|
||||
assert(p != NULL);
|
||||
|
||||
return containerof(p->node.next, struct prop, node);
|
||||
}
|
||||
|
||||
struct prop *prop_get_parent(struct prop *p) {
|
||||
assert(p != NULL);
|
||||
|
||||
return p->parent;
|
||||
}
|
||||
|
||||
enum prop_type prop_get_type(const struct prop *p) {
|
||||
assert(p != NULL);
|
||||
|
||||
return p->type;
|
||||
}
|
||||
|
||||
const char *prop_get_value_str(const struct prop *p) {
|
||||
assert(p != NULL);
|
||||
assert(p->type == PROP_STR);
|
||||
|
||||
return (const char *)p->bytes;
|
||||
}
|
||||
|
||||
struct prop *prop_search_child(struct prop *p, const char *name) {
|
||||
struct list_node *pos;
|
||||
struct prop *child;
|
||||
|
||||
assert(p != NULL);
|
||||
assert(name != NULL);
|
||||
|
||||
for (pos = p->children.head; pos != NULL; pos = pos->next) {
|
||||
child = containerof(pos, struct prop, node);
|
||||
|
||||
if (str_eq(child->name, name)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int prop_set_attr(struct prop *p, const char *key, const char *val) {
|
||||
struct list_node *pos;
|
||||
struct attr *a;
|
||||
int r;
|
||||
|
||||
assert(p != NULL);
|
||||
assert(key != NULL);
|
||||
assert(val != NULL);
|
||||
|
||||
for (pos = p->attrs.head; pos != NULL; pos = pos->next) {
|
||||
a = containerof(pos, struct attr, node);
|
||||
|
||||
if (str_eq(key, a->key) == 0) {
|
||||
return attr_set(a, val);
|
||||
}
|
||||
}
|
||||
|
||||
a = calloc(1, sizeof(*a));
|
||||
|
||||
if (a == NULL) {
|
||||
r = -ENOMEM;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
a->key = strdup(key);
|
||||
|
||||
if (a->key == NULL) {
|
||||
r = -ENOMEM;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
a->val = strdup(val);
|
||||
|
||||
if (a->val == NULL) {
|
||||
r = -ENOMEM;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
list_append(&p->attrs, &a->node);
|
||||
|
||||
a = NULL;
|
||||
r = 0;
|
||||
|
||||
end:
|
||||
free(a);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
const struct attr *attr_get_next_sibling(const struct attr *a) {
|
||||
const struct list_node *next;
|
||||
|
||||
assert(a != NULL);
|
||||
|
||||
next = a->node.next;
|
||||
|
||||
if (next == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return containerof(next, struct attr, node);
|
||||
}
|
||||
|
||||
const char *attr_get_key(const struct attr *a) {
|
||||
assert(a != NULL);
|
||||
|
||||
return a->key;
|
||||
}
|
||||
|
||||
const char *attr_get_val(const struct attr *a) {
|
||||
assert(a != NULL);
|
||||
|
||||
return a->val;
|
||||
}
|
||||
|
||||
static int attr_set(struct attr *a, const char *val) {
|
||||
char *replace;
|
||||
|
||||
replace = strdup(val);
|
||||
|
||||
if (replace == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
free(a->val);
|
||||
a->val = replace;
|
||||
|
||||
return 0;
|
||||
}
|
||||
32
573file/prop.h
Normal file
32
573file/prop.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "573file/prop-type.h"
|
||||
|
||||
#include "util/iobuf.h"
|
||||
|
||||
struct attr;
|
||||
struct prop;
|
||||
|
||||
int prop_alloc(struct prop **p, const char *name, enum prop_type type,
|
||||
const void *bytes, uint32_t nbytes);
|
||||
void prop_free(struct prop *p);
|
||||
void prop_append(struct prop *p, struct prop *child);
|
||||
void prop_borrow_value(const struct prop *p, struct const_iobuf *out);
|
||||
const char *prop_get_attr(const struct prop *p, const char *key);
|
||||
const struct attr *prop_get_first_attr(const struct prop *p);
|
||||
struct prop *prop_get_first_child(struct prop *p);
|
||||
const struct prop *prop_get_first_child_const(const struct prop *p);
|
||||
const char *prop_get_name(const struct prop *p);
|
||||
struct prop *prop_get_next_sibling(struct prop *p);
|
||||
const struct prop *prop_get_next_sibling_const(const struct prop *p);
|
||||
struct prop *prop_get_parent(struct prop *p);
|
||||
enum prop_type prop_get_type(const struct prop *p);
|
||||
const char *prop_get_value_str(const struct prop *p);
|
||||
struct prop *prop_search_child(struct prop *p, const char *name);
|
||||
int prop_set_attr(struct prop *p, const char *key, const char *val);
|
||||
|
||||
const struct attr *attr_get_next_sibling(const struct attr *a);
|
||||
const char *attr_get_key(const struct attr *a);
|
||||
const char *attr_get_val(const struct attr *a);
|
||||
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
13
README.md
Normal file
13
README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Saltytools
|
||||
|
||||
A toolkit for dumping and perhaps some day re-packing the proprietary file formats used by various Konami arcade games. Currently implemented using a very pedantic style of C99, these tools are small and UNIX-y, and are designed to be chained together into game-specific asset dumping pipelines by means of shell scripts.
|
||||
|
||||
# Similar projects
|
||||
|
||||
This toolkit is primarily intended to accommodate my own personal preferences and use cases, although maybe somebody else might find it useful as well. Similar tools with a richer feature set than this project can be found at the following location(s):
|
||||
|
||||
- https://github.com/DragonMinded/bemaniutils/
|
||||
|
||||
# License
|
||||
|
||||
The Unlicense (see LICENSE file for details).
|
||||
242
ifsdump/main.c
Normal file
242
ifsdump/main.c
Normal file
@@ -0,0 +1,242 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "573file/ifs.h"
|
||||
#include "573file/prop-xml-writer.h"
|
||||
#include "573file/prop.h"
|
||||
|
||||
#include "util/fs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static int ifs_dump_child(struct ifs *ifs, const struct prop *child,
|
||||
const char *parent_path);
|
||||
static int ifs_dump_dir(struct ifs *ifs, const struct prop *dirent,
|
||||
const char *path);
|
||||
static int ifs_dump_file(struct ifs *ifs, const struct prop *child,
|
||||
const char *path);
|
||||
static int ifs_dump_toc(struct ifs *ifs, const char *dir);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *infile;
|
||||
const char *outdir;
|
||||
struct ifs *ifs;
|
||||
int r;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: %s [infile] [outdir]\n", argv[0]);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile = argv[1];
|
||||
outdir = argv[2];
|
||||
ifs = NULL;
|
||||
|
||||
r = ifs_open(&ifs, infile);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = fs_mkdir(outdir);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = ifs_dump_toc(ifs, outdir);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = ifs_dump_child(ifs, ifs_get_root(ifs), outdir);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
ifs_close(ifs);
|
||||
|
||||
if (r < 0) {
|
||||
log_write("%s (%i)", strerror(-r), r);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int ifs_dump_toc(struct ifs *ifs, const char *dir) {
|
||||
const struct prop *toc;
|
||||
struct const_iobuf buf;
|
||||
char *path;
|
||||
char *xml;
|
||||
int r;
|
||||
|
||||
assert(ifs != NULL);
|
||||
assert(dir != NULL);
|
||||
|
||||
path = NULL;
|
||||
xml = NULL;
|
||||
|
||||
r = str_printf(&path, "%s/toc.xml", dir);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
toc = ifs_get_root(ifs);
|
||||
r = prop_xml_write(toc, &xml);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
buf.bytes = (uint8_t *)xml;
|
||||
buf.nbytes = strlen(xml);
|
||||
buf.pos = 0;
|
||||
|
||||
r = fs_write_file(path, &buf);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
free(xml);
|
||||
free(path);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ifs_dump_dir(struct ifs *ifs, const struct prop *parent,
|
||||
const char *path) {
|
||||
const struct prop *child;
|
||||
int r;
|
||||
|
||||
assert(ifs != NULL);
|
||||
assert(parent != NULL);
|
||||
assert(path != NULL);
|
||||
|
||||
for (child = ifs_dirent_get_first_child(parent); child != NULL;
|
||||
child = ifs_dirent_get_next_sibling(child)) {
|
||||
r = ifs_dump_child(ifs, child, path);
|
||||
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ifs_dump_child(struct ifs *ifs, const struct prop *child,
|
||||
const char *parent_path) {
|
||||
char *name;
|
||||
char *path;
|
||||
int r;
|
||||
|
||||
assert(ifs != NULL);
|
||||
assert(child != NULL);
|
||||
assert(parent_path != NULL);
|
||||
|
||||
name = NULL;
|
||||
path = NULL;
|
||||
|
||||
r = ifs_dirent_get_name(child, &name);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = str_printf(&path, "%s/%s", parent_path, name);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ifs_dirent_is_dir(child)) {
|
||||
r = fs_mkdir(path);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = ifs_dump_dir(ifs, child, path);
|
||||
} else {
|
||||
r = ifs_dump_file(ifs, child, path);
|
||||
}
|
||||
|
||||
end:
|
||||
free(path);
|
||||
free(name);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ifs_dump_file(struct ifs *ifs, const struct prop *child,
|
||||
const char *path) {
|
||||
struct const_iobuf src;
|
||||
void *bytes;
|
||||
size_t nbytes;
|
||||
FILE *f;
|
||||
int r;
|
||||
|
||||
assert(ifs != NULL);
|
||||
assert(child != NULL);
|
||||
assert(path != NULL);
|
||||
|
||||
bytes = NULL;
|
||||
f = NULL;
|
||||
|
||||
r = ifs_read_file(ifs, child, NULL, &nbytes);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
bytes = malloc(nbytes);
|
||||
|
||||
if (bytes == NULL) {
|
||||
r = -ENOMEM;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = ifs_read_file(ifs, child, bytes, &nbytes);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = fs_open(&f, path, "wb");
|
||||
|
||||
if (r < 0) {
|
||||
log_write("fopen %s: %s (%i)", path, strerror(-r), r);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
src.bytes = bytes;
|
||||
src.nbytes = nbytes;
|
||||
src.pos = 0;
|
||||
|
||||
r = fs_write(f, &src);
|
||||
|
||||
if (r < 0) {
|
||||
log_write("fwrite %s: %s (%i)", path, strerror(-r), r);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
fs_close(f);
|
||||
free(bytes);
|
||||
|
||||
return r;
|
||||
}
|
||||
12
ifsdump/meson.build
Normal file
12
ifsdump/meson.build
Normal file
@@ -0,0 +1,12 @@
|
||||
executable(
|
||||
'ifsdump',
|
||||
include_directories: inc,
|
||||
c_pch: '../precompiled.h',
|
||||
link_with: [
|
||||
_573file_lib,
|
||||
util_lib
|
||||
],
|
||||
sources: [
|
||||
'main.c'
|
||||
]
|
||||
)
|
||||
9
meson.build
Normal file
9
meson.build
Normal file
@@ -0,0 +1,9 @@
|
||||
project('saltytools', 'c', version: '0.1.0')
|
||||
|
||||
inc = include_directories('.')
|
||||
|
||||
subdir('573file')
|
||||
subdir('util')
|
||||
|
||||
subdir('ifsdump')
|
||||
subdir('xmldump')
|
||||
12
precompiled.h
Normal file
12
precompiled.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <assert.h> // IWYU pragma: keep
|
||||
#include <errno.h> // IWYU pragma: keep
|
||||
#include <stdarg.h> // IWYU pragma: keep
|
||||
#include <stdbool.h> // IWYU pragma: keep
|
||||
#include <stddef.h> // IWYU pragma: keep
|
||||
#include <stdint.h> // IWYU pragma: keep
|
||||
#include <stdio.h> // IWYU pragma: keep
|
||||
#include <stdlib.h> // IWYU pragma: keep
|
||||
#include <string.h> // IWYU pragma: keep
|
||||
|
||||
#include <sys/stat.h> // IWYU pragma: keep
|
||||
#include <unistd.h> // IWYU pragma: keep
|
||||
231
util/fs.c
Normal file
231
util/fs.c
Normal file
@@ -0,0 +1,231 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util/fs.h"
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
|
||||
int fs_open(FILE **out, const char *path, const char *mode) {
|
||||
FILE *f;
|
||||
|
||||
assert(out != NULL);
|
||||
assert(path != NULL);
|
||||
assert(mode != NULL);
|
||||
|
||||
*out = NULL;
|
||||
f = fopen(path, mode);
|
||||
|
||||
if (f == NULL) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*out = f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fs_close(FILE *f) {
|
||||
if (f != NULL) {
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
int fs_seek_to(FILE *f, size_t off) {
|
||||
int r;
|
||||
|
||||
assert(f != NULL);
|
||||
|
||||
r = fseek(f, off, SEEK_SET);
|
||||
|
||||
if (r != 0) {
|
||||
r = -errno;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int fs_read(FILE *f, struct iobuf *buf) {
|
||||
size_t nbytes;
|
||||
size_t nread;
|
||||
int r;
|
||||
|
||||
assert(f != NULL);
|
||||
assert(buf != NULL);
|
||||
assert(buf->pos <= buf->nbytes);
|
||||
|
||||
nbytes = buf->nbytes - buf->pos;
|
||||
nread = fread(buf->bytes + buf->pos, 1, nbytes, f);
|
||||
|
||||
if (nread < nbytes) {
|
||||
r = -errno;
|
||||
|
||||
if (r == 0) {
|
||||
r = -ENODATA;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
buf->pos += nbytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fs_write(FILE *f, struct const_iobuf *buf) {
|
||||
size_t nbytes;
|
||||
size_t nwrit;
|
||||
int r;
|
||||
|
||||
assert(f != NULL);
|
||||
assert(buf != NULL);
|
||||
assert(buf->pos <= buf->nbytes);
|
||||
|
||||
nbytes = buf->nbytes - buf->pos;
|
||||
nwrit = fwrite(buf->bytes + buf->pos, 1, nbytes, f);
|
||||
|
||||
if (nwrit < nbytes) {
|
||||
r = -errno;
|
||||
|
||||
if (r != 0) {
|
||||
log_write("Write failed: %s (%i)", strerror(-r), r);
|
||||
} else {
|
||||
log_write("Short write: %zu < %zu", nwrit, nbytes);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
buf->pos += nbytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fs_read_file(const char *path, void **out_bytes, size_t *out_nbytes) {
|
||||
FILE *f;
|
||||
struct iobuf buf;
|
||||
void *bytes;
|
||||
long nbytes;
|
||||
int r;
|
||||
|
||||
assert(path != NULL);
|
||||
assert(out_bytes != NULL);
|
||||
assert(out_nbytes != NULL);
|
||||
|
||||
bytes = NULL;
|
||||
*out_bytes = NULL;
|
||||
*out_nbytes = 0;
|
||||
|
||||
r = fs_open(&f, path, "rb");
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = fseek(f, 0, SEEK_END);
|
||||
|
||||
if (r != 0) {
|
||||
r = -errno;
|
||||
log_write("fseek(SEEK_END): %s (%i)", strerror(-r), r);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
nbytes = ftell(f);
|
||||
r = fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (r != 0) {
|
||||
r = -errno;
|
||||
log_write("fseek(SEEK_SET): %s (%i)", strerror(-r), r);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
bytes = malloc(nbytes);
|
||||
|
||||
if (bytes == NULL) {
|
||||
r = -ENOMEM;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
buf.bytes = bytes;
|
||||
buf.nbytes = nbytes;
|
||||
buf.pos = 0;
|
||||
|
||||
r = fs_read(f, &buf);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
*out_bytes = bytes;
|
||||
*out_nbytes = nbytes;
|
||||
bytes = NULL;
|
||||
|
||||
end:
|
||||
free(bytes);
|
||||
fs_close(f);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int fs_write_file(const char *path, struct const_iobuf *buf) {
|
||||
FILE *f;
|
||||
int r;
|
||||
|
||||
assert(path != NULL);
|
||||
assert(buf != NULL);
|
||||
|
||||
f = NULL;
|
||||
r = fs_open(&f, path, "wb");
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = fs_write(f, buf);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
fs_close(f);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int fs_mkdir(const char *path) {
|
||||
struct stat s;
|
||||
int r;
|
||||
|
||||
assert(path != NULL);
|
||||
|
||||
r = stat(path, &s);
|
||||
|
||||
if (r == 0) {
|
||||
/* Early return on _success_, not failure. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = -errno;
|
||||
|
||||
if (r != -ENOENT) {
|
||||
log_write("stat(%s): %i (%s)", path, r, strerror(-r));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
r = mkdir(path, 0755);
|
||||
|
||||
if (r != 0) {
|
||||
r = -errno;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
15
util/fs.h
Normal file
15
util/fs.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "util/iobuf.h"
|
||||
|
||||
int fs_open(FILE **out, const char *path, const char *mode);
|
||||
void fs_close(FILE *f);
|
||||
int fs_seek_to(FILE *f, size_t off);
|
||||
int fs_read(FILE *f, struct iobuf *buf);
|
||||
int fs_write(FILE *f, struct const_iobuf *buf);
|
||||
|
||||
int fs_read_file(const char *path, void **bytes, size_t *nbytes);
|
||||
int fs_write_file(const char *path, struct const_iobuf *buf);
|
||||
int fs_mkdir(const char *path);
|
||||
336
util/iobuf.c
Normal file
336
util/iobuf.c
Normal file
@@ -0,0 +1,336 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/iobuf.h"
|
||||
|
||||
static int iobuf_bounds_check(const struct const_iobuf *buf, size_t nbytes);
|
||||
|
||||
static int iobuf_bounds_check(const struct const_iobuf *buf, size_t nbytes) {
|
||||
assert(buf != NULL);
|
||||
|
||||
if (buf->pos + nbytes > buf->nbytes) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_slice(struct const_iobuf *dest, struct const_iobuf *src,
|
||||
size_t nbytes) {
|
||||
int r;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
r = iobuf_bounds_check(src, nbytes);
|
||||
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
dest->bytes = src->bytes + src->pos;
|
||||
dest->nbytes = nbytes;
|
||||
dest->pos = 0;
|
||||
|
||||
src->pos += nbytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_align_read(struct const_iobuf *buf, size_t alignment) {
|
||||
size_t mask;
|
||||
size_t new_pos;
|
||||
|
||||
assert(buf != NULL);
|
||||
assert(alignment != 0);
|
||||
|
||||
mask = alignment - 1;
|
||||
|
||||
assert((alignment & mask) == 0);
|
||||
|
||||
new_pos = (buf->pos + mask) & ~mask;
|
||||
|
||||
if (new_pos > buf->nbytes) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
buf->pos = new_pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_peek(struct const_iobuf *src, void *bytes, size_t nbytes) {
|
||||
int r;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(bytes != NULL);
|
||||
|
||||
r = iobuf_bounds_check(src, nbytes);
|
||||
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
memcpy(bytes, &src->bytes[src->pos], nbytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_read(struct const_iobuf *src, void *bytes, size_t nbytes) {
|
||||
int r;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(bytes != NULL);
|
||||
|
||||
r = iobuf_bounds_check(src, nbytes);
|
||||
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
memcpy(bytes, src->bytes + src->pos, nbytes);
|
||||
src->pos += nbytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_read_8(struct const_iobuf *src, uint8_t *out) {
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint8_t) > src->nbytes) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
*out = src->bytes[src->pos++];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_read_be16(struct const_iobuf *src, uint16_t *out) {
|
||||
uint16_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint16_t) > src->nbytes) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
value = src->bytes[src->pos++] << 8;
|
||||
value |= src->bytes[src->pos++];
|
||||
|
||||
*out = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_read_be32(struct const_iobuf *src, uint32_t *out) {
|
||||
uint32_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint32_t) > src->nbytes) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
value = src->bytes[src->pos++] << 24;
|
||||
value |= src->bytes[src->pos++] << 16;
|
||||
value |= src->bytes[src->pos++] << 8;
|
||||
value |= src->bytes[src->pos++];
|
||||
|
||||
*out = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_read_be64(struct const_iobuf *src, uint64_t *out) {
|
||||
uint64_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint64_t) > src->nbytes) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
value = ((uint64_t)src->bytes[src->pos++]) << 56;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 48;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 40;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 32;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 24;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 16;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 8;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]);
|
||||
|
||||
*out = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_read_le16(struct const_iobuf *src, uint16_t *out) {
|
||||
uint16_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint16_t) > src->nbytes) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
value = src->bytes[src->pos++];
|
||||
value |= src->bytes[src->pos++] << 8;
|
||||
|
||||
*out = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_read_le32(struct const_iobuf *src, uint32_t *out) {
|
||||
uint32_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint32_t) > src->nbytes) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
value = src->bytes[src->pos++];
|
||||
value |= src->bytes[src->pos++] << 8;
|
||||
value |= src->bytes[src->pos++] << 16;
|
||||
value |= src->bytes[src->pos++] << 24;
|
||||
|
||||
*out = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iobuf_read_le64(struct const_iobuf *src, uint64_t *out) {
|
||||
uint64_t value;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
if (src->pos + sizeof(uint64_t) > src->nbytes) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
value = ((uint64_t)src->bytes[src->pos++]);
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 8;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 16;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 24;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 32;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 40;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 48;
|
||||
value |= ((uint64_t)src->bytes[src->pos++]) << 56;
|
||||
|
||||
*out = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iobuf_write(struct iobuf *dest, const void *bytes, size_t nbytes) {
|
||||
assert(dest != NULL);
|
||||
assert(bytes != NULL || nbytes == 0);
|
||||
|
||||
if (dest->pos + nbytes <= dest->nbytes) {
|
||||
memcpy(dest->bytes + dest->pos, bytes, nbytes);
|
||||
}
|
||||
|
||||
dest->pos += nbytes;
|
||||
}
|
||||
|
||||
void iobuf_write_8(struct iobuf *dest, uint8_t value) {
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + 1 <= dest->nbytes) {
|
||||
dest->bytes[dest->pos] = value;
|
||||
}
|
||||
|
||||
dest->pos += 1;
|
||||
}
|
||||
|
||||
void iobuf_write_be16(struct iobuf *dest, uint16_t value) {
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + 2 <= dest->nbytes) {
|
||||
dest->bytes[dest->pos + 0] = value >> 8;
|
||||
dest->bytes[dest->pos + 1] = value;
|
||||
}
|
||||
|
||||
dest->pos += 2;
|
||||
}
|
||||
|
||||
void iobuf_write_be32(struct iobuf *dest, uint32_t value) {
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + 4 <= dest->nbytes) {
|
||||
dest->bytes[dest->pos + 0] = value >> 24;
|
||||
dest->bytes[dest->pos + 1] = value >> 16;
|
||||
dest->bytes[dest->pos + 2] = value >> 8;
|
||||
dest->bytes[dest->pos + 3] = value;
|
||||
}
|
||||
|
||||
dest->pos += 4;
|
||||
}
|
||||
|
||||
void iobuf_write_be64(struct iobuf *dest, uint64_t value) {
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + 8 <= dest->nbytes) {
|
||||
dest->bytes[dest->pos + 0] = value >> 56;
|
||||
dest->bytes[dest->pos + 1] = value >> 48;
|
||||
dest->bytes[dest->pos + 2] = value >> 40;
|
||||
dest->bytes[dest->pos + 3] = value >> 32;
|
||||
dest->bytes[dest->pos + 4] = value >> 24;
|
||||
dest->bytes[dest->pos + 5] = value >> 16;
|
||||
dest->bytes[dest->pos + 6] = value >> 8;
|
||||
dest->bytes[dest->pos + 7] = value;
|
||||
}
|
||||
|
||||
dest->pos += 8;
|
||||
}
|
||||
|
||||
void iobuf_write_le16(struct iobuf *dest, uint16_t value) {
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + 2 <= dest->nbytes) {
|
||||
dest->bytes[dest->pos + 0] = value;
|
||||
dest->bytes[dest->pos + 1] = value >> 8;
|
||||
}
|
||||
|
||||
dest->pos += 2;
|
||||
}
|
||||
|
||||
void iobuf_write_le32(struct iobuf *dest, uint32_t value) {
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + 4 <= dest->nbytes) {
|
||||
dest->bytes[dest->pos + 0] = value;
|
||||
dest->bytes[dest->pos + 1] = value >> 8;
|
||||
dest->bytes[dest->pos + 2] = value >> 16;
|
||||
dest->bytes[dest->pos + 3] = value >> 24;
|
||||
}
|
||||
|
||||
dest->pos += 4;
|
||||
}
|
||||
|
||||
void iobuf_write_le64(struct iobuf *dest, uint64_t value) {
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->pos + 8 <= dest->nbytes) {
|
||||
dest->bytes[dest->pos + 0] = value;
|
||||
dest->bytes[dest->pos + 1] = value >> 8;
|
||||
dest->bytes[dest->pos + 2] = value >> 16;
|
||||
dest->bytes[dest->pos + 3] = value >> 24;
|
||||
dest->bytes[dest->pos + 4] = value >> 32;
|
||||
dest->bytes[dest->pos + 5] = value >> 40;
|
||||
dest->bytes[dest->pos + 6] = value >> 48;
|
||||
dest->bytes[dest->pos + 7] = value >> 56;
|
||||
}
|
||||
|
||||
dest->pos += 8;
|
||||
}
|
||||
39
util/iobuf.h
Normal file
39
util/iobuf.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct iobuf {
|
||||
uint8_t *bytes;
|
||||
size_t nbytes;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
struct const_iobuf {
|
||||
const uint8_t *bytes;
|
||||
size_t nbytes;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
int iobuf_slice(struct const_iobuf *dest, struct const_iobuf *src,
|
||||
size_t nbytes);
|
||||
int iobuf_align_read(struct const_iobuf *buf, size_t alignment);
|
||||
|
||||
int iobuf_peek(struct const_iobuf *src, void *bytes, size_t nbytes);
|
||||
int iobuf_read(struct const_iobuf *src, void *bytes, size_t nbytes);
|
||||
int iobuf_read_8(struct const_iobuf *src, uint8_t *value);
|
||||
int iobuf_read_be16(struct const_iobuf *src, uint16_t *value);
|
||||
int iobuf_read_be32(struct const_iobuf *src, uint32_t *value);
|
||||
int iobuf_read_be64(struct const_iobuf *src, uint64_t *value);
|
||||
int iobuf_read_le16(struct const_iobuf *src, uint16_t *value);
|
||||
int iobuf_read_le32(struct const_iobuf *src, uint32_t *value);
|
||||
int iobuf_read_le64(struct const_iobuf *src, uint64_t *value);
|
||||
|
||||
void iobuf_write(struct iobuf *dest, const void *bytes, size_t nbytes);
|
||||
void iobuf_write_8(struct iobuf *dest, uint8_t value);
|
||||
void iobuf_write_be16(struct iobuf *dest, uint16_t value);
|
||||
void iobuf_write_be32(struct iobuf *dest, uint32_t value);
|
||||
void iobuf_write_be64(struct iobuf *dest, uint64_t value);
|
||||
void iobuf_write_le16(struct iobuf *dest, uint16_t value);
|
||||
void iobuf_write_le32(struct iobuf *dest, uint32_t value);
|
||||
void iobuf_write_le64(struct iobuf *dest, uint64_t value);
|
||||
46
util/list.c
Normal file
46
util/list.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
void list_append(struct list *list, struct list_node *node) {
|
||||
assert(list != NULL);
|
||||
assert(node != NULL);
|
||||
|
||||
node->next = NULL;
|
||||
|
||||
if (list->tail != NULL) {
|
||||
list->tail->next = node;
|
||||
} else {
|
||||
list->head = node;
|
||||
}
|
||||
|
||||
list->tail = node;
|
||||
}
|
||||
|
||||
void list_remove(struct list *list, struct list_node *node) {
|
||||
struct list_node *pos;
|
||||
struct list_node *prev;
|
||||
|
||||
assert(list != NULL);
|
||||
|
||||
for (prev = NULL, pos = list->head; pos != NULL;
|
||||
prev = pos, pos = pos->next) {
|
||||
if (pos == node) {
|
||||
if (prev != NULL) {
|
||||
prev->next = node->next;
|
||||
} else {
|
||||
list->head = node->next;
|
||||
}
|
||||
|
||||
if (node->next == NULL) {
|
||||
list->tail = prev;
|
||||
}
|
||||
|
||||
node->next = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
util/list.h
Normal file
15
util/list.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct list {
|
||||
struct list_node *head;
|
||||
struct list_node *tail;
|
||||
};
|
||||
|
||||
struct list_node {
|
||||
struct list_node *next;
|
||||
};
|
||||
|
||||
void list_append(struct list *list, struct list_node *node);
|
||||
void list_remove(struct list *list, struct list_node *node);
|
||||
25
util/log.c
Normal file
25
util/log.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static void log_vwrite_(const char *func, const char *fmt, va_list ap);
|
||||
|
||||
void log_write_(const char *func, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
log_vwrite_(func, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void log_vwrite_(const char *func, const char *fmt, va_list ap) {
|
||||
printf("%s: ", func);
|
||||
vprintf(fmt, ap);
|
||||
puts("");
|
||||
}
|
||||
|
||||
void log_error_(const char *func, const char *file, int line, int r) {
|
||||
log_write_(func, "%s:%i: %s (%i)", file, line, strerror(-r), r);
|
||||
}
|
||||
10
util/log.h
Normal file
10
util/log.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/macro.h"
|
||||
|
||||
#define log_write(...) log_write_(__func__, __VA_ARGS__)
|
||||
#define log_error(r) log_error_(__func__, __FILE__, __LINE__, r);
|
||||
|
||||
void log_write_(const char *func, const char *fmt, ...)
|
||||
gcc_attribute((format(printf, 2, 3)));
|
||||
void log_error_(const char *func, const char *file, int line, int r);
|
||||
14
util/macro.h
Normal file
14
util/macro.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define gcc_attribute(x) __attribute__(x)
|
||||
#else
|
||||
#define gcc_attribute(x)
|
||||
#endif
|
||||
|
||||
#define containerof(member, outer_type, outer_member) \
|
||||
((outer_type *)(((char *)member) - offsetof(outer_type, outer_member)))
|
||||
|
||||
#define lengthof(x) (sizeof(x) / sizeof(x[0]))
|
||||
18
util/meson.build
Normal file
18
util/meson.build
Normal file
@@ -0,0 +1,18 @@
|
||||
util_lib = static_library(
|
||||
'util',
|
||||
include_directories: [inc],
|
||||
c_pch: '../precompiled.h',
|
||||
sources: [
|
||||
'fs.c',
|
||||
'fs.h',
|
||||
'iobuf.c',
|
||||
'iobuf.h',
|
||||
'list.c',
|
||||
'list.h',
|
||||
'log.c',
|
||||
'log.h',
|
||||
'macro.h',
|
||||
'str.c',
|
||||
'str.h',
|
||||
]
|
||||
)
|
||||
123
util/str.c
Normal file
123
util/str.c
Normal file
@@ -0,0 +1,123 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/str.h"
|
||||
|
||||
bool str_eq(const char *lhs, const char *rhs) {
|
||||
if (lhs == NULL || rhs == NULL) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
return strcmp(lhs, rhs) == 0;
|
||||
}
|
||||
|
||||
int str_printf(char **out, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
assert(out != NULL);
|
||||
assert(fmt != NULL);
|
||||
|
||||
va_start(ap, fmt);
|
||||
*out = NULL;
|
||||
r = str_vprintf(out, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int str_vprintf(char **out, const char *fmt, va_list ap) {
|
||||
va_list ap2;
|
||||
char *chars;
|
||||
int len;
|
||||
int len2;
|
||||
|
||||
*out = NULL;
|
||||
|
||||
va_copy(ap2, ap);
|
||||
len = vsnprintf(NULL, 0, fmt, ap2);
|
||||
va_end(ap2);
|
||||
|
||||
chars = malloc(len + 1);
|
||||
|
||||
if (chars == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
va_copy(ap2, ap);
|
||||
len2 = vsnprintf(chars, len + 1, fmt, ap2);
|
||||
va_end(ap2);
|
||||
|
||||
assert(len2 == len);
|
||||
|
||||
*out = chars;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void strbuf_printf(struct strbuf *dest, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(fmt != NULL);
|
||||
|
||||
va_start(ap, fmt);
|
||||
strbuf_vprintf(dest, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void strbuf_vprintf(struct strbuf *dest, const char *fmt, va_list ap) {
|
||||
size_t avail;
|
||||
char *chars;
|
||||
int result;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->chars == NULL || dest->pos < dest->nchars);
|
||||
assert(fmt != NULL);
|
||||
|
||||
if (dest->chars == NULL) {
|
||||
avail = 0;
|
||||
chars = NULL;
|
||||
} else {
|
||||
avail = dest->nchars - dest->pos;
|
||||
chars = dest->chars + dest->pos;
|
||||
}
|
||||
|
||||
result = vsnprintf(chars, avail, fmt, ap);
|
||||
|
||||
assert(dest->chars == NULL || result < avail);
|
||||
|
||||
dest->pos += result;
|
||||
}
|
||||
|
||||
void strbuf_putc(struct strbuf *dest, char c) {
|
||||
assert(dest != NULL);
|
||||
|
||||
if (dest->chars != NULL) {
|
||||
assert(dest->pos + 1 < dest->nchars);
|
||||
|
||||
dest->chars[dest->pos + 0] = c;
|
||||
dest->chars[dest->pos + 1] = '\0';
|
||||
}
|
||||
|
||||
dest->pos++;
|
||||
}
|
||||
|
||||
void strbuf_puts(struct strbuf *dest, const char *str) {
|
||||
size_t len;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(str != NULL);
|
||||
|
||||
len = strlen(str);
|
||||
|
||||
if (dest->chars != NULL) {
|
||||
assert(dest->pos + len < dest->nchars);
|
||||
|
||||
memcpy(dest->chars + dest->pos, str, len + 1);
|
||||
}
|
||||
|
||||
dest->pos += len;
|
||||
}
|
||||
25
util/str.h
Normal file
25
util/str.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/macro.h"
|
||||
|
||||
struct strbuf {
|
||||
char *chars;
|
||||
size_t nchars;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
bool str_eq(const char *lhs, const char *rhs);
|
||||
|
||||
int str_printf(char **out, const char *fmt, ...)
|
||||
gcc_attribute((format(printf, 2, 3)));
|
||||
int str_vprintf(char **out, const char *fmt, va_list ap);
|
||||
|
||||
void strbuf_printf(struct strbuf *dest, const char *fmt, ...)
|
||||
gcc_attribute((format(printf, 2, 3)));
|
||||
void strbuf_vprintf(struct strbuf *dest, const char *fmt, va_list ap);
|
||||
void strbuf_putc(struct strbuf *dest, char c);
|
||||
void strbuf_puts(struct strbuf *dest, const char *str);
|
||||
96
xmldump/main.c
Normal file
96
xmldump/main.c
Normal file
@@ -0,0 +1,96 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "573file/prop-binary-reader.h"
|
||||
#include "573file/prop-xml-writer.h"
|
||||
#include "573file/prop.h"
|
||||
|
||||
#include "util/fs.h"
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *infile;
|
||||
const char *outfile;
|
||||
struct const_iobuf buf;
|
||||
struct prop *p;
|
||||
void *bytes;
|
||||
char *xml;
|
||||
size_t nbytes;
|
||||
FILE *f;
|
||||
FILE *f_dest;
|
||||
int r;
|
||||
|
||||
if (argc < 2 || argc > 3) {
|
||||
fprintf(stderr, "Usage: %s [infile] <outfile>\n", argv[0]);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
f = NULL;
|
||||
bytes = NULL;
|
||||
xml = NULL;
|
||||
p = NULL;
|
||||
|
||||
infile = argv[1];
|
||||
|
||||
if (argc > 2) {
|
||||
outfile = argv[2];
|
||||
} else {
|
||||
outfile = NULL;
|
||||
}
|
||||
|
||||
r = fs_read_file(infile, &bytes, &nbytes);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = prop_binary_parse(&p, bytes, nbytes);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = prop_xml_write(p, &xml);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (outfile != NULL) {
|
||||
r = fs_open(&f, outfile, "w");
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
f_dest = f;
|
||||
} else {
|
||||
f_dest = stdout;
|
||||
}
|
||||
|
||||
buf.bytes = (uint8_t *)xml;
|
||||
buf.nbytes = strlen(xml);
|
||||
buf.pos = 0;
|
||||
|
||||
r = fs_write(f_dest, &buf);
|
||||
|
||||
if (r < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
fs_close(f);
|
||||
free(xml);
|
||||
prop_free(p);
|
||||
free(bytes);
|
||||
|
||||
if (r < 0) {
|
||||
log_write("%s (%i)", strerror(-r), r);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
12
xmldump/meson.build
Normal file
12
xmldump/meson.build
Normal file
@@ -0,0 +1,12 @@
|
||||
executable(
|
||||
'xmldump',
|
||||
include_directories: inc,
|
||||
c_pch: '../precompiled.h',
|
||||
link_with: [
|
||||
_573file_lib,
|
||||
util_lib
|
||||
],
|
||||
sources: [
|
||||
'main.c'
|
||||
]
|
||||
)
|
||||
Reference in New Issue
Block a user