256 lines
7.4 KiB
C
256 lines
7.4 KiB
C
/* ************************************************************************** */
|
|
/* _____ _ */
|
|
/* parse/lang.c |_ _|__ _ _| |__ ___ _ _ */
|
|
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
|
/* | | (_) | |_| | | | | __/ |_| | */
|
|
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
|
/* Last updated: 2016/12/01 13:33:45 |___/ */
|
|
/* */
|
|
/* ************************************************************************** */
|
|
#include <libg1m/internals.h>
|
|
|
|
/* ************************************************************************** */
|
|
/* fx language files parsing */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* g1m_parse_lang:
|
|
* Parse fx language files.
|
|
*
|
|
* @arg handle the libg1m handle.
|
|
* @arg stream the stream to parse from.
|
|
* @return the error code (0 if ok).
|
|
*/
|
|
|
|
int g1m_parse_lang(g1m_t *handle, FILE *stream,
|
|
struct standard_header *std)
|
|
{
|
|
(void)std;
|
|
/* set handle type */
|
|
handle->messages = NULL;
|
|
|
|
/* read the subheader */
|
|
DREAD(hd, g1l_subheader)
|
|
|
|
/* correct the endianness */
|
|
uint_fast16_t num = be16toh(hd.message_count) + 1;
|
|
|
|
/* log */
|
|
log_info("%" PRIuFAST16 " messages to read", num);
|
|
|
|
/* beat the best, read the rest! */
|
|
size_t data_size = std->filesize - sizeof(struct standard_header)
|
|
- sizeof(struct g1l_subheader);
|
|
uint8_t data[data_size];
|
|
READ(data, data_size)
|
|
|
|
/* allocate tab */
|
|
handle->messages = malloc(sizeof(char*) * num);
|
|
handle->count = 0;
|
|
handle->_size = num;
|
|
if (!handle->messages)
|
|
return (g1m_error_alloc);
|
|
|
|
/* get the offset table */
|
|
uint16_t *offsets = (uint16_t*)data;
|
|
char *messages = (char*)(offsets + num);
|
|
|
|
/* read messages */
|
|
for (uint_fast16_t i = 0; i < num; i++) {
|
|
handle->messages[i] = NULL;
|
|
if (offsets[i] == (uint16_t)-1) {
|
|
log_info("[#%" PRIuFAST16 "] -", i);
|
|
handle->count++;
|
|
continue ;
|
|
}
|
|
|
|
/* correct offset */
|
|
offsets[i] = be16toh(offsets[i]);
|
|
|
|
/* log */
|
|
log_info("[#%" PRIuFAST16 "] '%s' (0x%" PRIu16 ")", i,
|
|
&messages[offsets[i]], offsets[i]);
|
|
|
|
/* store */
|
|
handle->messages[i] = strdup(&messages[offsets[i]]);
|
|
if (!handle->messages[i]) goto fail;
|
|
handle->count++;
|
|
}
|
|
|
|
/* no error */
|
|
return (0);
|
|
|
|
/* omg fail! */
|
|
fail:
|
|
for (int i = 0; i < handle->count; i++)
|
|
free(handle->messages[i]);
|
|
free(handle->messages);
|
|
handle->messages = NULL;
|
|
return (g1m_error_alloc);
|
|
}
|
|
|
|
/* ************************************************************************** */
|
|
/* cg language files parsing */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* g1m_parse_lang_cg_content:
|
|
* Parse the content of the message zone size for a G3L language file.
|
|
*
|
|
* @arg handle the libg1m handle.
|
|
* @arg stream the stream to read from.
|
|
* @arg zonesize the message zone size.
|
|
* @arg pchecksum pointer to the checksum to contribute to.
|
|
* @return the error code (0 if ok).
|
|
*/
|
|
|
|
static int g1m_parse_lang_cg_content(g1m_t *handle, FILE *stream,
|
|
uint_fast32_t zonesize, uint32_t *pchecksum)
|
|
{
|
|
int err; uint32_t checksum = *pchecksum;
|
|
/* correct the handle type */
|
|
handle->type = g1m_type_lang;
|
|
|
|
/* read the subheader */
|
|
DREAD(lhd, g3l_lang_header)
|
|
checksum = g1m_checksum32(&lhd, sizeof(struct g3l_lang_header), checksum);
|
|
|
|
/* get the messages table */
|
|
uint_fast32_t num = be32toh(lhd.num) + 1;
|
|
uint32_t off[num + 1]; off[num] = zonesize;
|
|
READ(off, num * sizeof(uint32_t))
|
|
checksum = g1m_checksum32(off, num * sizeof(uint32_t), checksum);
|
|
|
|
/* prepare for next */
|
|
uint32_t *offsets = &off[1]; num--;
|
|
log_info("%" PRIuFAST32 " messages to read", num);
|
|
|
|
/* get index of first message */
|
|
uint_fast32_t i;
|
|
for (i = 0; i < num && offsets[i] == (uint32_t)-1; i++)
|
|
handle->messages[i] = NULL;
|
|
offsets[i] = be32toh(offsets[i]);
|
|
|
|
/* get and copy the language name */
|
|
off[0] = be32toh(off[0]);
|
|
if (off[0] == (uint32_t)-1) {
|
|
log_fatal("First offset shouldn't be blank!");
|
|
return (g1m_error_magic);
|
|
}
|
|
size_t langname_size = offsets[i] - off[0];
|
|
uint8_t langname[langname_size];
|
|
READ(langname, langname_size)
|
|
handle->title[16] = 0;
|
|
strncpy(handle->title, (char*)langname, 16);
|
|
log_info("Language name is '%s'", handle->title);
|
|
|
|
/* prepare the handle messages table */
|
|
handle->messages = malloc(sizeof(char*) * num);
|
|
if (!handle->messages) return (g1m_error_alloc);
|
|
handle->count = 0;
|
|
handle->_size = num;
|
|
|
|
/* main loop */
|
|
uint_fast32_t next;
|
|
for (; i < num; i = next) {
|
|
/* find and correct next offset */
|
|
for (next = i + 1; offsets[next] == (uint32_t)-1; next++)
|
|
handle->messages[next] = NULL;
|
|
offsets[next] = be32toh(offsets[next]);
|
|
|
|
/* read the message */
|
|
size_t msg_size = offsets[next] - offsets[i];
|
|
uint8_t msg[msg_size];
|
|
GREAD(msg, msg_size)
|
|
checksum = g1m_checksum32(msg, msg_size, checksum);
|
|
|
|
/* copy it */
|
|
handle->messages[i] = malloc(msg_size + 1);
|
|
if (!handle->messages[i]) goto fail;
|
|
handle->messages[i][msg_size] = 0;
|
|
strncpy(handle->messages[i], (char*)msg, msg_size);
|
|
handle->count += (next - i);
|
|
|
|
/* log it */
|
|
log_info("[#%04" PRIuFAST32 "] '%s'", i, handle->messages[i]);
|
|
}
|
|
|
|
/* done */
|
|
*pchecksum = checksum;
|
|
return (0);
|
|
|
|
fail:
|
|
g1m_free_content(handle);
|
|
return (err);
|
|
}
|
|
|
|
/**
|
|
* g1m_parse_lang_cg:
|
|
* Parse fx-CG language files.
|
|
*
|
|
* @arg handle the libg1m handle.
|
|
* @arg stream the stream to parse from.
|
|
* @arg std the standard header.
|
|
* @return the error code (0 if ok).
|
|
*/
|
|
|
|
int g1m_parse_lang_cg(g1m_t *handle, FILE *stream,
|
|
struct standard_header *std)
|
|
{
|
|
/* set handle type */
|
|
handle->messages = NULL;
|
|
|
|
/* read the subheader */
|
|
DREAD(hd, g3l_subheader)
|
|
|
|
/* correct the endianness */
|
|
hd.checksum = be32toh(hd.checksum);
|
|
hd.message_zone_size = be32toh(hd.message_zone_size);
|
|
|
|
/* log */
|
|
log_info("internal name is '%.8s'", hd.internal_name);
|
|
log_info("language name is '%.16s'", hd.language_name);
|
|
log_info("language salutation is '%.16s'", hd.language_salutation);
|
|
log_info("version is '%.10s'", hd.version);
|
|
log_info("datetime is '%.14s'", hd.datetime);
|
|
log_info("g3l filename is '%.16s'", hd.g3l_filename);
|
|
|
|
/* precalculate the checksum beginning */
|
|
uint32_t checksum =
|
|
sizeof(struct standard_header) * 255
|
|
- g1m_checksum32(std, sizeof(struct standard_header), 0) +
|
|
g1m_checksum32(&hd.magic, sizeof(struct g3l_subheader) - 4, 0);
|
|
|
|
/* read and decode the message zone */
|
|
int err = 0;
|
|
size_t zonesize = hd.filesize - sizeof(struct standard_header)
|
|
- sizeof(struct g3l_subheader) - sizeof(struct g3l_footer);
|
|
if (hd.magic[0] == 0x04)
|
|
err = g1m_parse_lang_cg_content(handle, stream, zonesize, &checksum);
|
|
else return (g1m_error_magic);
|
|
if (err) return (g1m_error_magic);
|
|
|
|
/* check the checksum */
|
|
if (hd.checksum && hd.checksum != checksum) {
|
|
log_fatal("header checksum was invalid");
|
|
goto fail;
|
|
}
|
|
|
|
/* read the footer */
|
|
GDREAD(footer, g3l_footer)
|
|
footer.checksum = be32toh(footer.checksum);
|
|
if (footer.checksum != hd.checksum) {
|
|
log_fatal("footer checksum was invalid!");
|
|
log_info("header checksum is %" PRIu32 ","
|
|
" calculated checksum is %" PRIu32,
|
|
hd.checksum, checksum);
|
|
goto fail;
|
|
}
|
|
|
|
/* no error */
|
|
return (0);
|
|
|
|
/* omg fail! */
|
|
fail:
|
|
g1m_free_content(handle);
|
|
return (g1m_error_alloc);
|
|
}
|