/* ************************************************************************** */ /* _____ _ */ /* parse/eact.c |_ _|__ _ _| |__ ___ _ _ */ /* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */ /* | | (_) | |_| | | | | __/ |_| | */ /* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ /* Last updated: 2016/12/01 13:33:45 |___/ */ /* */ /* ************************************************************************** */ #include /* ************************************************************************** */ /* Content parsing */ /* ************************************************************************** */ /* line content parsing function prototype */ static int eact_parse_line(g1m_line_t *handle, uint8_t *buf, size_t size, uint_fast8_t type); /** * eact_parse_content_eact: * Parse an EACT in an EACT, from the subheader. * * @arg handle the handle. * @arg buf the buffer. * @arg bufsize the buffer size. * @return the error code (0 if ok). */ static int eact_parse_content_eact(g1m_line_t *handle, uint8_t *buf, size_t bufsize) { int err; /* initialize the handle */ handle->type = g1m_linetype_eact; handle->count = 0; handle->_size = 0; handle->lines = NULL; /* check if the buffer is non-empty */ if (!bufsize) { log_info("Content was name-only."); return (0); } /* get e-act subheader */ if (bufsize < sizeof(struct eact_eactheader)) return (g1m_error_eof); struct eact_eactheader ehd; memcpy(&ehd, buf, sizeof(struct eact_eactheader)); /* correct endianess */ ehd.line_count = be32toh(ehd.line_count); /* get the line descriptors * there is actually a "bonus" line descriptor at the end, but the * fact that we're using offsets lets us unaware of it. */ if (bufsize < sizeof(struct eact_eactheader) + (ehd.line_count + 1) * sizeof(struct line_descriptor)) return (g1m_error_eof); struct line_descriptor *lds = (void*)&buf[sizeof(struct eact_eactheader)]; /* prepare the handle */ handle->count = 0; handle->_size = ehd.line_count; handle->lines = malloc(sizeof(g1m_line_t*) * handle->_size); if (!handle->lines) return (g1m_error_alloc); bzero(handle->lines, sizeof(g1m_line_t*) * handle->_size); /* browse the lines */ log_info("%" PRIu32 " lines to browse", ehd.line_count); if (ehd.line_count) lds[0].entry_offset = be32toh(lds[0].entry_offset << 8); for (uint_fast32_t i = 0; i < ehd.line_count; i++) { /* allocate line */ g1m_line_t *line = malloc(sizeof(g1m_line_t)); err = g1m_error_alloc; if (!line) goto fail; /* get line size * for this, we'll calculate the distance to the next line offset. * if there is no next line (last line), then the data left will be * attributed to the line. */ size_t linesize; if (i == ehd.line_count - 1) linesize = bufsize - lds[i].entry_offset; else { /* correct endianess of the next element */ lds[i + 1].entry_offset = be32toh(lds[i + 1].entry_offset << 8); /* then calculate */ linesize = lds[i + 1].entry_offset - lds[i].entry_offset; } /* store current info */ uint_fast8_t entry_type = lds[i].entry_type; uint_fast32_t entry_offset = lds[i].entry_offset; /* check if buffer is big enough */ err = g1m_error_eof; if (entry_offset + linesize > bufsize) goto loop_fail; /* look for the line type */ err = eact_parse_line(line, &buf[entry_offset], linesize, entry_type); if (err) goto loop_fail; /* store and continue */ handle->lines[handle->count++] = line; continue; loop_fail: g1m_free_line_content(line); free(line); goto fail; } /* no error */ return (0); fail: g1m_free_line_content(handle); return (err); } /* ************************************************************************** */ /* Content Type correspondance list */ /* ************************************************************************** */ /* Correspondance type */ struct eact_content_type_corresp { const char *type; int (*parse)(g1m_line_t*, uint8_t*, size_t); }; /* Correspondance list */ static struct eact_content_type_corresp eact_content_types[] = { {"@EACT\0\0", eact_parse_content_eact}, {} }; /* ************************************************************************** */ /* Line parsing */ /* ************************************************************************** */ /** * eact_parse_line_content: * Parse an E-Activity content. * * @arg handle the handle. * @arg buf the buffer. * @arg size the buffer size. * @return the error code (0 if ok). */ static int eact_parse_line_content(g1m_line_t *handle, uint8_t *buf, size_t size) { /* read content header */ size_t could_read = min(size, sizeof(struct eact_contentheader)); if (could_read < 9) return (g1m_error_eof); struct eact_contentheader hd = {}; memcpy(&hd, buf, could_read); /* store info */ bzero(handle->name, 17); size_t namesize = max(could_read - 8, 16); strncpy(handle->name, (char*)hd.name, namesize); /* log info */ log_info("Type is '%.8s'", hd.type); log_info("Name is '%s'", handle->name); /* prepare for next */ buf += could_read; size -= could_read; /* check for content type */ struct eact_content_type_corresp *ctype = eact_content_types; while (ctype->parse) { if (!memcmp(hd.type, ctype->type, 8)) break ; ctype++; } if (!ctype->parse) { log_error("Unknown content type: '%.8s'", hd.type); return (0); } /* then parse */ int err; if ((err = (*ctype->parse)(handle, buf, size))) return (err); /* otherwise, no error. */ return (0); } /** * eact_parse_line_calculation: * Parse a calculation. * * @arg handle the handle. * @arg buf the buffer. * @arg size the buffer size. * @return the error code (0 if ok). */ static int eact_parse_line_calculation(g1m_line_t *handle, uint8_t *buf, size_t size) { (void)handle; (void)buf; (void)size; /* TODO */ log_info("Calculation raw data is:"); logm_info(buf, size); return (0); } /** * eact_parse_line_result: * Parse a calculation result. * * @arg handle the handle. * @arg buf the buffer. * @arg size the buffer size. * @return the error code (0 if ok). */ static int eact_parse_line_result(g1m_line_t *handle, uint8_t *buf, size_t size) { (void)handle; (void)buf; (void)size; /* TODO */ log_info("Calculation result raw data is:"); logm_info(buf, size); return (0); } /** * eact_parse_line_stdheading: * Parse standard heading line. * * @arg handle the handle. * @arg buf the buffer. * @arg size the buffer size. * @return the error code (0 if ok). */ static int eact_parse_line_stdheading(g1m_line_t *handle, uint8_t *buf, size_t size) { /* log */ log_info("Standard heading raw data is:"); logm_info(buf, size); /* set handle info */ handle->type = g1m_linetype_text; handle->content = strdup((char*)buf); if (!handle->content) return (g1m_error_alloc); /* no error */ return (0); } /** * eact_parse_line_picture: * Parse picture line. * * @arg handle the handle. * @arg buf the buffer. * @arg size the buffer size. * @return the error code (0 if ok). */ static int eact_parse_line_picture(g1m_line_t *handle, uint8_t *buf, size_t size) { /* log */ log_info("Picture raw data is:"); logm_info(buf, size); /* big endian to host endianness */ FONTCHARACTER *s = (FONTCHARACTER*)buf; FONTCHARACTER *p = s - 1; while (*(++p)) *p = be16toh(*p); /* get the size of the multi-byte string */ size_t sz = g1m_fcstombs(NULL, s, 0); if (sz == (size_t)-1) return (g1m_error_magic); /* make the string */ handle->content = malloc(sizeof(FONTCHARACTER) * (sz + 1)); if (!handle->content) return (g1m_error_alloc); g1m_fcstombs(handle->content, s, 0); /* don't forget to set the handle type! */ handle->type = g1m_linetype_picture; /* no error */ return (0); } /** * eact_parse_line_text: * Parse text line. * * @arg handle the handle. * @arg buf the buffer. * @arg size the buffer size. * @return the error code (0 if ok). */ static int eact_parse_line_text(g1m_line_t *handle, uint8_t *buf, size_t size) { /* log */ log_info("Text raw data is:"); logm_info(buf, size); /* set handle info */ handle->type = g1m_linetype_text; handle->content = strdup((char*)buf); if (!handle->content) return (g1m_error_alloc); /* TODO: manage text coloration */ /* no error */ return (0); } /** * eact_parse_line_code: * Parse code line. * * @arg handle the handle. * @arg buf the buffer. * @arg size the buffer size. * @return the error code (0 if ok). */ static int eact_parse_line_code(g1m_line_t *handle, uint8_t *buf, size_t size) { (void)handle; (void)buf; (void)size; /* TODO */ log_info("Code raw data is:"); logm_info(buf, size); return (0); } /* ************************************************************************** */ /* Line Type correspondance list */ /* ************************************************************************** */ /* Correspondance type */ struct eact_line_type_corresp { int rawtype; int (*parse)(g1m_line_t*, uint8_t*, size_t); const char *info; }; /* All correspondances */ static struct eact_line_type_corresp eact_line_types[] = { {eact_ltype_calc, eact_parse_line_calculation, "calculation"}, {eact_ltype_calc_result, eact_parse_line_result, "calculation result"}, {eact_ltype_content, eact_parse_line_content, "content"}, {eact_ltype_stdheading, eact_parse_line_stdheading, "standard heading"}, {eact_ltype_picture, eact_parse_line_picture, "picture"}, {eact_ltype_text, eact_parse_line_text, "pure text"}, {eact_ltype_code, eact_parse_line_code, "text mixed with functions"}, {} }; /** * eact_parse_line: * Parse a line [content]. * * @arg handle the handle. * @arg buf the buffer. * @arg size the buffer size. * @arg type the type. * @return the error code (0 if ok). */ static int eact_parse_line(g1m_line_t *handle, uint8_t *buf, size_t size, uint_fast8_t type) { /* initialize handle */ handle->type = 0x00; /* look for the line type */ struct eact_line_type_corresp *linetype = eact_line_types; while (linetype->parse) { if (linetype->rawtype == type) break; linetype++; } if (!linetype->parse) { log_error("unknown line type: %02x", type); return (0); } /* act */ log_info("line type is '%s' (0x%02x)", linetype->info, type); return ((*linetype->parse)(handle, buf, size)); } /* ************************************************************************** */ /* Main parsing functions */ /* ************************************************************************** */ /** * g1m_parse_eact: * Parse an EACT. * * Thanks to Julese50 for his help on e-acts parsing. * * @arg handle the handle. * @arg stream the stream to parse from. * @return the error code (0 if ok). */ int g1m_parse_eact(g1m_t * handle, FILE *stream, struct standard_header *std) { (void)std; /* parse the header */ DREAD(hd, eact_header) /* correct endianess */ hd.filesize = be32toh(hd.filesize); hd.setup_area_size = be32toh(hd.setup_area_size); hd.eact_version = be32toh(hd.eact_version); hd.os_version = be32toh(hd.os_version); /* find out the size of the setup area */ log_info("E-Activity version is '%s'.", hd.eact_version == EACT_G1E ? "g1e" : hd.eact_version == EACT_G2E ? "g2e" : "g3e"); log_info("Setup area size is 0x%" PRIx32 " bytes long.", hd.setup_area_size); /* check if is CG */ if (hd.eact_version == EACT_G3E) handle->platform = g1m_platform_cg; /* skip the setup area */ SKIP(hd.setup_area_size) /* get content buffer */ size_t bufsize = hd.filesize - sizeof(struct standard_header) - sizeof(struct eact_header) - hd.setup_area_size; uint8_t buf[bufsize]; READ(&buf, bufsize) /* prepare handle */ handle->line = &handle->_linedata; /* parse content */ return (eact_parse_line_content(handle->line, buf, bufsize)); }