mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-22 05:10:02 +01:00
Fix some .psb crashes
This commit is contained in:
parent
4e390bd61a
commit
567c0af18e
@ -16,6 +16,7 @@ typedef struct {
|
|||||||
const char* uniq; /* unique name, typically same as file without extension (optional) */
|
const char* uniq; /* unique name, typically same as file without extension (optional) */
|
||||||
const char* wav; /* same as file (optional) */
|
const char* wav; /* same as file (optional) */
|
||||||
} psb_temp_t;
|
} psb_temp_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
psb_temp_t* tmp;
|
psb_temp_t* tmp;
|
||||||
psb_codec_t codec;
|
psb_codec_t codec;
|
||||||
@ -504,25 +505,29 @@ fail:
|
|||||||
|
|
||||||
|
|
||||||
static int prepare_name(psb_header_t* psb) {
|
static int prepare_name(psb_header_t* psb) {
|
||||||
char* buf = psb->readable_name;
|
|
||||||
int buf_size = sizeof(psb->readable_name);
|
|
||||||
const char* main_name = psb->tmp->voice;
|
const char* main_name = psb->tmp->voice;
|
||||||
const char* sub_name = psb->tmp->uniq;
|
const char* sub_name = psb->tmp->uniq;
|
||||||
int main_len;
|
char* buf = psb->readable_name;
|
||||||
|
int buf_size = sizeof(psb->readable_name);
|
||||||
|
|
||||||
|
if (!main_name) /* shouldn't happen */
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!sub_name)
|
if (!sub_name)
|
||||||
sub_name = psb->tmp->wav;
|
sub_name = psb->tmp->wav;
|
||||||
if (!sub_name)
|
if (!sub_name)
|
||||||
sub_name = psb->tmp->file;
|
sub_name = psb->tmp->file;
|
||||||
|
|
||||||
if (!main_name) /* shouldn't happen */
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
/* sometimes we have main="bgm01", sub="bgm01.wav" = detect and ignore */
|
/* sometimes we have main="bgm01", sub="bgm01.wav" = detect and ignore */
|
||||||
main_len = strlen(main_name);
|
if (sub_name) {
|
||||||
if (sub_name && strncmp(main_name, sub_name, main_len) == 0) {
|
int main_len = strlen(main_name);
|
||||||
if (sub_name[main_len] == '\0' || strcmp(sub_name + main_len, ".wav") == 0)
|
int sub_len = strlen(sub_name);
|
||||||
sub_name = NULL;
|
|
||||||
|
if (main_len > sub_len && strncmp(main_name, sub_name, main_len) == 0) {
|
||||||
|
if (sub_name[main_len] == '\0' || strcmp(sub_name + main_len, ".wav") == 0)
|
||||||
|
sub_name = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sub_name) {
|
if (sub_name) {
|
||||||
@ -733,7 +738,7 @@ fail:
|
|||||||
* Keys are (seemingly) stored in text order.
|
* Keys are (seemingly) stored in text order.
|
||||||
*/
|
*/
|
||||||
static int parse_psb(STREAMFILE* sf, psb_header_t* psb) {
|
static int parse_psb(STREAMFILE* sf, psb_header_t* psb) {
|
||||||
psb_temp_t tmp;
|
psb_temp_t tmp = {0};
|
||||||
psb_context_t* ctx = NULL;
|
psb_context_t* ctx = NULL;
|
||||||
psb_node_t nroot, nvoice;
|
psb_node_t nroot, nvoice;
|
||||||
float version;
|
float version;
|
||||||
|
@ -11,32 +11,34 @@
|
|||||||
* also https://github.com/number201724/psbfile and https://github.com/UlyssesWu/FreeMote
|
* also https://github.com/number201724/psbfile and https://github.com/UlyssesWu/FreeMote
|
||||||
*
|
*
|
||||||
* PSB defines a header with offsets to sections within the header, binary format being type-value (where type could be
|
* PSB defines a header with offsets to sections within the header, binary format being type-value (where type could be
|
||||||
* int8/int16/float/array/object/etc). Example:
|
* int8/int16/float/list/object/etc). Example:
|
||||||
* 21 // object: root (x2 info lists + items)
|
* 21 // object: root (x2 listN + other data)
|
||||||
* 0D 04 0D 06,0B,0D,0E // list8[4]: key indexes ("id/spec/version/voice")
|
* 0D 04 0D 06,0B,0D,0E // list8[4]: key indexes (#0 "id", #1 "spec", #2 "version", #3 "voice"; found in a separate "key names" table)
|
||||||
* 0D 04 0D 00,02,04,09 // list8[4]: byte offsets of next 4 items
|
* 0D 04 0D 00,02,04,09 // list8[4]: byte offsets of next 4 items
|
||||||
* 15 02 // 0 string8: string#2 ("spec")
|
* 15 02 // #0 string8: string key #2 ("pc")
|
||||||
* 1E 5C8F823F // 1 float32: 1.02
|
* 1E 5C8F823F // #1 float32: 1.02
|
||||||
* 05 02 // 2 int8: 2
|
* 05 02 // #2 int8: 2
|
||||||
* 21 // 3 object
|
* 21 // #3 object
|
||||||
* 0D 02 0D 02,05 // list8[2]: key indexes
|
* 0D 02 0D 02,05 // list8[2]: key indexes
|
||||||
* 0D 02 0D 00,02 // list8[2]: byte offsets
|
* 0D 02 0D 00,02 // list8[2]: byte offsets
|
||||||
* 19 00 // 0 resource8: resource#0 (subfile)
|
* 19 00 // #0 resource8: resource #0 (offset/size found in a separate "data resource" table)
|
||||||
* 20 // 1 array: loops
|
* 20 // #1 array: loops
|
||||||
* 0D 02 0D 00,04 // list8[2]
|
* 0D 02 0D 00,04 // list8[2]
|
||||||
* 07 D69107 // 0 int24
|
* 07 D69107 // #0 int24
|
||||||
* 07 31A45C // 1 int24
|
* 07 31A45C // #1 int24
|
||||||
|
*
|
||||||
|
* A game would then position on root object (offset in header) and ask for key "version".
|
||||||
|
* M2 lib finds the index for that key (#2), skips to that offset (0x04), reads type float32, returns 1.02
|
||||||
*/
|
*/
|
||||||
//TODO: som
|
//TODO: add validations on buf over max size (like reading u32 on edge buf[len-1])
|
||||||
//TODO: add validations on buf over max size
|
|
||||||
//TODO: validate strings table ends with null (buf[max - 1] = '\0')
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* DEFS */
|
/* DEFS */
|
||||||
|
|
||||||
#define PSB_VERSION2 2 /* older (x360/ps3) games */
|
#define PSB_VERSION2 2 /* older (x360/ps3) games */
|
||||||
#define PSB_VERSION3 3 /* current games */
|
#define PSB_VERSION3 3 /* current games */
|
||||||
#define PSB_MAX_HEADER 0x40000 /* max seen ~0x1000 */
|
#define PSB_MAX_HEADER 0x40000 /* max seen ~0x1000 (alloc'd) */
|
||||||
|
|
||||||
|
|
||||||
/* Internal type used in binary data, that defines bytes used to store value.
|
/* Internal type used in binary data, that defines bytes used to store value.
|
||||||
@ -94,10 +96,10 @@ typedef enum {
|
|||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int bytes; /* total bytes (including headers) to skip this list */
|
int bytes; /* total bytes (including headers) to skip this list */
|
||||||
int count; /* number of entries */
|
int count; /* number of entries */
|
||||||
int esize; /* size per entry */
|
int esize; /* size per entry */
|
||||||
uint8_t* edata; /* start of entries */
|
uint8_t* edata; /* start of entries */
|
||||||
} list_t;
|
} list_t;
|
||||||
|
|
||||||
struct psb_context_t {
|
struct psb_context_t {
|
||||||
@ -109,21 +111,22 @@ struct psb_context_t {
|
|||||||
|
|
||||||
uint32_t strings_list_offset;
|
uint32_t strings_list_offset;
|
||||||
uint32_t strings_data_offset;
|
uint32_t strings_data_offset;
|
||||||
uint32_t data_offsets_offset; //todo resources
|
uint32_t data_offsets_offset;
|
||||||
uint32_t data_sizes_offset;
|
uint32_t data_sizes_offset;
|
||||||
|
|
||||||
uint32_t data_offset; //todo resources
|
uint32_t data_offset; /* also "resources" */
|
||||||
uint32_t root_offset;
|
uint32_t root_offset; /* initial node */
|
||||||
uint32_t unknown; /* hash/crc? (v3) */
|
uint32_t unknown; /* hash/crc? (v3) */
|
||||||
|
|
||||||
/* main buf and derived stuff*/
|
/* main buf and derived stuff */
|
||||||
uint8_t* buf;
|
uint8_t* buf;
|
||||||
|
int buf_len;
|
||||||
list_t strings_list;
|
list_t strings_list;
|
||||||
uint8_t* strings_data;
|
uint8_t* strings_data;
|
||||||
|
int strings_data_len;
|
||||||
list_t data_offsets_list;
|
list_t data_offsets_list;
|
||||||
list_t data_sizes_list;
|
list_t data_sizes_list;
|
||||||
|
|
||||||
/* keys buf */
|
/* keys buf */
|
||||||
char* keys;
|
char* keys;
|
||||||
int* keys_pos;
|
int* keys_pos;
|
||||||
@ -189,7 +192,7 @@ static int list_init(list_t* lst, uint8_t* buf) {
|
|||||||
default:
|
default:
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get entry info (0D + 00,01,02,03) */
|
/* get entry info (0D + 00,01,02,03) */
|
||||||
entry_itype = buf[1 + count_size];
|
entry_itype = buf[1 + count_size];
|
||||||
switch (entry_itype) {
|
switch (entry_itype) {
|
||||||
@ -318,7 +321,6 @@ psb_context_t* psb_init(STREAMFILE* sf) {
|
|||||||
psb_context_t* ctx;
|
psb_context_t* ctx;
|
||||||
uint8_t header[0x2c];
|
uint8_t header[0x2c];
|
||||||
int bytes;
|
int bytes;
|
||||||
uint32_t buf_len;
|
|
||||||
|
|
||||||
ctx = calloc(1, sizeof(psb_context_t));
|
ctx = calloc(1, sizeof(psb_context_t));
|
||||||
if (!ctx) goto fail;
|
if (!ctx) goto fail;
|
||||||
@ -364,26 +366,38 @@ psb_context_t* psb_init(STREAMFILE* sf) {
|
|||||||
ctx->root_offset >= ctx->data_offset)
|
ctx->root_offset >= ctx->data_offset)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
|
||||||
/* copy data for easier access */
|
/* copy data for easier access */
|
||||||
buf_len = ctx->data_offset;
|
ctx->buf_len = (int)ctx->data_offset;
|
||||||
if (buf_len > PSB_MAX_HEADER)
|
if (ctx->buf_len < 0 || ctx->buf_len > PSB_MAX_HEADER)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
ctx->buf = malloc(buf_len);
|
ctx->buf = malloc(ctx->buf_len);
|
||||||
if (!ctx->buf) goto fail;
|
if (!ctx->buf) goto fail;
|
||||||
|
|
||||||
bytes = read_streamfile(ctx->buf, 0x00, buf_len, sf);
|
bytes = read_streamfile(ctx->buf, 0x00, ctx->buf_len, sf);
|
||||||
if (bytes != buf_len) goto fail;
|
if (bytes != ctx->buf_len) goto fail;
|
||||||
|
|
||||||
|
|
||||||
|
/* lists pointing to string block */
|
||||||
if (!list_init(&ctx->strings_list, &ctx->buf[ctx->strings_list_offset]))
|
if (!list_init(&ctx->strings_list, &ctx->buf[ctx->strings_list_offset]))
|
||||||
goto fail;
|
goto fail;
|
||||||
ctx->strings_data = &ctx->buf[ctx->strings_data_offset];
|
|
||||||
|
|
||||||
|
/* block of plain c-strings (all null-terminated, including last) */
|
||||||
|
ctx->strings_data = &ctx->buf[ctx->strings_data_offset];
|
||||||
|
ctx->strings_data_len = ctx->data_offsets_offset - ctx->strings_data_offset;
|
||||||
|
if (ctx->strings_data_len < 0 || ctx->strings_data[ctx->strings_data_len - 1] != '\0') /* just in case to avoid overruns */
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
|
||||||
|
/* lists to access resources */
|
||||||
if (!list_init(&ctx->data_offsets_list, &ctx->buf[ctx->data_offsets_offset]))
|
if (!list_init(&ctx->data_offsets_list, &ctx->buf[ctx->data_offsets_offset]))
|
||||||
goto fail;
|
goto fail;
|
||||||
if (!list_init(&ctx->data_sizes_list, &ctx->buf[ctx->data_sizes_offset]))
|
if (!list_init(&ctx->data_sizes_list, &ctx->buf[ctx->data_sizes_offset]))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
|
||||||
|
/* prepare key strings for easier handling */
|
||||||
if (!init_keys(ctx))
|
if (!init_keys(ctx))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -397,7 +411,7 @@ fail:
|
|||||||
void psb_close(psb_context_t* ctx) {
|
void psb_close(psb_context_t* ctx) {
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
free(ctx->keys_pos);
|
free(ctx->keys_pos);
|
||||||
free(ctx->keys);
|
free(ctx->keys);
|
||||||
free(ctx->buf);
|
free(ctx->buf);
|
||||||
@ -532,7 +546,7 @@ int psb_node_by_index(const psb_node_t* node, int index, psb_node_t* p_out) {
|
|||||||
p_out->data = &buf[1 + keys.bytes + offsets.bytes + skip];
|
p_out->data = &buf[1 + keys.bytes + offsets.bytes + skip];
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -574,7 +588,7 @@ fail:
|
|||||||
const char* psb_node_get_key(const psb_node_t* node, int index) {
|
const char* psb_node_get_key(const psb_node_t* node, int index) {
|
||||||
uint8_t* buf;
|
uint8_t* buf;
|
||||||
int pos;
|
int pos;
|
||||||
|
|
||||||
if (!node || !node->ctx || !node->data)
|
if (!node || !node->ctx || !node->data)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -592,7 +606,7 @@ const char* psb_node_get_key(const psb_node_t* node, int index) {
|
|||||||
pos = node->ctx->keys_pos[keys_index];
|
pos = node->ctx->keys_pos[keys_index];
|
||||||
return &node->ctx->keys[pos];
|
return &node->ctx->keys[pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -651,8 +665,11 @@ psb_result_t psb_node_get_result(psb_node_t* node) {
|
|||||||
index = item_get_int(size, &buf[1]);
|
index = item_get_int(size, &buf[1]);
|
||||||
skip = list_get_entry(&node->ctx->strings_list, index);
|
skip = list_get_entry(&node->ctx->strings_list, index);
|
||||||
|
|
||||||
res.str = (const char*)&node->ctx->strings_data[skip]; /* null-terminated */
|
res.str = (const char*)&node->ctx->strings_data[skip]; /* null-terminated (validated on open) */
|
||||||
//todo test max strlen to see if it's null-terminated
|
if (skip >= node->ctx->strings_data_len) { /* shouldn't happen */
|
||||||
|
vgm_logi("PSBLIB: bad skip over strings\n");
|
||||||
|
res.str = "";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,7 +682,7 @@ psb_result_t psb_node_get_result(psb_node_t* node) {
|
|||||||
|
|
||||||
res.data.offset = list_get_entry(&node->ctx->data_offsets_list, index);
|
res.data.offset = list_get_entry(&node->ctx->data_offsets_list, index);
|
||||||
res.data.size = list_get_entry(&node->ctx->data_sizes_list, index);
|
res.data.size = list_get_entry(&node->ctx->data_sizes_list, index);
|
||||||
|
|
||||||
res.data.offset += node->ctx->data_offset;
|
res.data.offset += node->ctx->data_offset;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -754,8 +771,8 @@ psb_data_t psb_node_get_data(const psb_node_t* node, const char* key) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
return psb_node_get_result(&out).data;
|
return psb_node_get_result(&out).data;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int psb_node_exists(const psb_node_t* node, const char* key) {
|
int psb_node_exists(const psb_node_t* node, const char* key) {
|
||||||
psb_node_t out;
|
psb_node_t out;
|
||||||
if (!psb_node_by_key(node, key, &out))
|
if (!psb_node_by_key(node, key, &out))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user