From 43c58008fad13f5f853f369bbe3e6426ba16bae9 Mon Sep 17 00:00:00 2001 From: "Thomas \"Cakeisalie5\" Touhey" Date: Fri, 17 Mar 2017 22:23:55 +0100 Subject: [PATCH] Finished rewriting of most things, corrected Prizm/CP headers support, still a weird bug to solve --- AUTHORS.md | 3 +- include/libg1m.h | 20 ++- include/libg1m/format/std.h | 116 +++++++++++- include/libg1m/format/std/addin.h | 100 +---------- include/libg1m/format/std/fkey.h | 17 +- include/libg1m/format/std/lang.h | 72 +------- include/libg1m/formatutils.h | 11 +- include/libg1m/handle.h | 6 +- include/libg1m/internals.h | 48 +++-- src/core/open.c | 19 +- src/decode/cas.c | 26 ++- src/decode/casemul.c | 23 ++- src/decode/main.c | 14 +- src/decode/std.c | 137 ++++++++++----- src/decode/std/addin.c | 283 ++++++++++++++---------------- src/decode/std/eact.c | 31 ++-- src/decode/std/fkey.c | 108 ++++++++---- src/decode/std/lang.c | 232 +++++++----------------- src/decode/std/mcs.c | 19 +- src/decode/std/picture.c | 21 +-- src/manage/handle.c | 224 ++++++++++++++++++----- src/type/std.c | 46 +++-- src/type/sub.c | 94 ++++++++++ src/utils/date.c | 50 ++++++ src/utils/skip.c | 4 +- src/utils/version.c | 47 +++++ tools/write-header-config | 22 +-- 27 files changed, 1018 insertions(+), 775 deletions(-) create mode 100644 src/type/sub.c create mode 100644 src/utils/date.c create mode 100644 src/utils/version.c diff --git a/AUTHORS.md b/AUTHORS.md index 8812daf..0633086 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -13,9 +13,10 @@ Thanks to: - Teamfx for complementary information; - KermMartian (from Cemetech) for its reverse engineering on Prizm-related formats and test files; +- Amazonka (from Cemetech) for its research on G3L/G3N; - Florian Birée for its regrouped documentation (Casetta), like for tokens; - Yves-Marie Morgan for opening Casemul's source code; - Tom Wheeley for making CaS a free software; - Him and Tom Lynn for documenting the legacy protocol (9700); -- Zezombye (from Planète Casio) for its research on some MCS formats; +- Zezombye (from Planète Casio) for its research on some MCS file formats; - Other people at **Planète Casio** for their support! diff --git a/include/libg1m.h b/include/libg1m.h index 63e60fd..58bcccd 100644 --- a/include/libg1m.h +++ b/include/libg1m.h @@ -77,7 +77,7 @@ extern const char *g1m_error_strings[]; /* Main functions */ /* ************************************************************************** */ /* open and free a handle */ -extern int g1m_decode(g1m_t *handle, const char *path, g1m_buffer_t *buffer, +extern int g1m_decode(g1m_t **handle, const char *path, g1m_buffer_t *buffer, g1m_type_t allowed_types); extern void g1m_free(g1m_t *handle); @@ -90,8 +90,13 @@ extern int g1m_fopen(g1m_t **handle, const char *path, FILE *stream, #endif /* Make a handle */ -extern int g1m_make_mcs(g1m_t **h, int count); -extern int g1m_make_picture(g1m_t **h, unsigned int width, unsigned int height); +extern int g1m_make_mcs(g1m_t **handle, int count); +extern int g1m_make_fkey(g1m_t **handle, g1m_platform_t platform, int count); +extern int g1m_make_lang(g1m_t **handle, g1m_platform_t platform, int count); +extern int g1m_make_picture(g1m_t **handle, + unsigned int width, unsigned int height); +extern int g1m_make_addin(g1m_t **h, g1m_platform_t platform, + const g1m_version_t *version, const time_t *created); /* ************************************************************************** */ /* Main MCS functions */ /* ************************************************************************** */ @@ -133,6 +138,15 @@ extern int g1m_decode_casfile_part(g1m_mcsfile_t *file, g1m_buffer_t *buffer); extern int g1m_mcs_insert(g1m_t *handle, const g1m_mcshead_t *head, g1m_mcsfile_t **tofile); +/* ************************************************************************** */ +/* Miscallaneous utilities */ +/* ************************************************************************** */ +/* Version decoding/encoding */ +extern int g1m_decode_version(const char *raw, g1m_version_t *version); + +/* Date decoding/encoding */ +extern int g1m_decode_date(const char *raw, time_t *date); + # ifdef __cplusplus } # endif diff --git a/include/libg1m/format/std.h b/include/libg1m/format/std.h index c073ebc..71f810d 100644 --- a/include/libg1m/format/std.h +++ b/include/libg1m/format/std.h @@ -75,7 +75,6 @@ struct standard_header { * * We also thought the subtype was only one-byte long, but the C2P came along * with its "c2p\0\0\0" subtype. - * * All main ID/types correspondances are in the `src/utils/type.c` file. * From a user program, you can use the functions * in `include/libg1m/formatutils.h`. @@ -83,7 +82,120 @@ struct standard_header { * After the Standard Header is read and the type is read, we have parts, * each with their own subheaders and their own subtilities. * - * Where do you want to start? Pick your poison. */ + * Some Prizm/Classpad-related formats (language, fkeys, add-ins) use a + * common subheader followed by a platform-specific subheader (which is the + * same size of Prizm and Classpad, but doesn't seem to have the same + * field organization). Here is the common subheader structure: */ + +struct standard_subheader { + /* checksum */ + uint32_t checksum; + + /* file type: + * - 0x01: add-in; + * - 0x02: function keys; + * - 0x04: language files; */ + uint8_t filetype; + + /* platform: + * - 0x00: fx-CP; + * - 0x01: Prizm */ + uint8_t platform; + + /* unknown */ + uint8_t _unknown0[8]; + + /* control: + * - for a G3A: filesize - 0x7000 - 4 + * - for a C1A: filesize - 0x1000 - 4 */ + uint32_t control; + + /* unknown */ + uint8_t _unknown1[4]; + + /* message zone size - unreliable (valid could have zero) */ + uint32_t message_zone_size; + + /* model; for C1A : GY437 */ + uint8_t models[6]; + + /* title/language name */ + uint8_t title[28]; + + /* filesize */ + uint32_t filesize; + + /* internal name */ + uint8_t internal_name[11]; + + /* language labels */ + uint8_t labels[8][24]; + + /* eAct strip flag (0x00: can't be used, 0x01: can be used) */ + uint8_t eact_strip_flag; + + /* unknown */ + uint8_t _unknown2[4]; + + /* version: MM.mm.ffff */ + uint8_t version[10]; + + /* unknown */ + uint8_t _unknown3[2]; + + /* timestamp: YYYY.MMDD.HHmm */ + uint8_t timestamp[14]; +}; + +/* Here is the Prizm-specific subheader: */ + +struct _prizm_subheader { + /* unknown */ + uint8_t _unknown4[38]; + + /* eAct strip labels */ + uint8_t eact_strip_labels[8][36]; + + /* eAct icon */ + uint8_t icon[0x300]; + + /* unknown stuff */ + uint8_t _unknown5[0x90C]; + + /* language name (null terminated?) */ + uint8_t language_name[16]; + + /* language salutation (null terminated?) */ + uint8_t language_salutation[16]; + + /* filename (extension included) */ + uint8_t filename[0x144]; +}; + +/* And here is the Classpad-specific subheader: */ + +struct _classpad_subheader { + /* unknown */ + uint8_t _unknown4[0x46]; + + /* the C1A filename */ + uint8_t filename[0x144]; + + /* unknown */ + uint8_t _unknown5[0x2C]; + + /* icon (maybe 46x30 pixels? packed 1-bit) */ + uint8_t icon[172]; + + /* unknown */ + uint8_t _unknown3[0xC54]; +}; + +/* Also, if there is a Standard Subheader, there is a footer at the end of the + * file, which is only made of a 32-bit checksum that should be equal to + * the subheader checksum. + * + * Then, where do you want to start? Pick your poison. */ # pragma pack() # include diff --git a/include/libg1m/format/std/addin.h b/include/libg1m/format/std/addin.h index 2ada09b..1ecbad9 100644 --- a/include/libg1m/format/std/addin.h +++ b/include/libg1m/format/std/addin.h @@ -80,116 +80,26 @@ struct g1a_subheader { /* ************************************************************************** */ /* Classpad and Prizm add-ins (C1A, G3A) */ /* ************************************************************************** */ -/* The two formats are identified the same way in the standard header, but - * some of the header parts are different, until the header just stops being - * the same. +/* The two formats both have the Standard Subheader, then, for the C1A, + * the Classpad-specific subheader, and for the G3A, the Prizm-specific + * subheader. * - * Here's the common parts of their headers: */ - -struct addin_cp_subheader { - /* byte checksum: 0x0000 to 0x001F (standard header) - * and 0x0024 to EOF (everything after this checksum), minus 4. */ - uint32_t checksum; - - /* magic sequence to identify addin type: - * - {0x01, 0x01} for the Prizm (G3A); - * - {0x01, 0x00} for C1A. */ - uint8_t magic[2]; - - /* undocumented */ - uint8_t _unknown[8]; - - /* control: - * - for a G3A: filesize - 0x7000 - 4 - * - for a C1A: filesize - 0x1000 - 4*/ - uint32_t control; - - /* undocumented */ - uint8_t _unknown2[8]; - - /* for C1A: "GY437" */ - uint8_t model[6]; - - /* title (zero terminated) */ - uint8_t title[16]; - - /* undocumented */ - uint8_t undocumented3[12]; - - /* filesize */ - uint32_t filesize; - - /* internal name (preceded by @, filled up with zeros */ - uint8_t internal_name[11]; - - /* language labels */ - uint8_t language_1_label[24]; - uint8_t language_2_label[24]; - uint8_t language_3_label[24]; - uint8_t language_4_label[24]; - uint8_t language_5_label[24]; - uint8_t language_6_label[24]; - uint8_t language_7_label[24]; - uint8_t language_8_label[24]; - - /* eAct strip flag (0x00: cannot be used, 0x01: can be used) */ - uint8_t eact_strip_flag; - - /* some null bytes */ - uint8_t zeroes[4]; - - /* version: "01.00.0000" then zeroes */ - uint8_t version[12]; - - /* timestamp: "2012.0903.1652" */ - uint8_t stamp[14]; -}; - -/* Then from here, the two formats diverge. - * G3A subheader goes like this: */ + * After this, the G3A subheader looks like this: */ # define G3A_ICON_WIDTH 92 # define G3A_ICON_HEIGHT 64 struct g3a_subheader { - /* null bytes, again */ - uint8_t zeroes2[38]; - - /* eAct strip labels */ - uint8_t eact_strip_label_1[36]; - uint8_t eact_strip_label_2[36]; - uint8_t eact_strip_label_3[36]; - uint8_t eact_strip_label_4[36]; - uint8_t eact_strip_label_5[36]; - uint8_t eact_strip_label_6[36]; - uint8_t eact_strip_label_7[36]; - uint8_t eact_strip_label_8[36]; - - /* eAct icon */ - uint8_t icon[0x300]; - - /* unused */ - uint8_t unused[0x92c]; - - /* G3A filename (seriously?!) */ - uint8_t g3a_filename[0x144]; - /* selected and unselected icon image */ uint8_t selected_icon_image[0x3000]; uint8_t unselected_icon_image[0x3000]; }; -/* And the C1A header goes like this: */ +/* And the C1A subheader goes like this: */ # define C1A_ICON_WIDTH 46 # define C1A_ICON_HEIGHT 30 struct c1a_subheader { /* unknown bytes */ - uint8_t _unknown[0x46]; - - /* the C1A filename - the size is guessed with the other formats aside. */ - uint8_t filename[0x144]; - - /* again, unknown bytes */ uint8_t _unknown2[0x2C]; /* this is an approximation (46x30 pixels, packed 1-bit) */ diff --git a/include/libg1m/format/std/fkey.h b/include/libg1m/format/std/fkey.h index 93b0657..0b61fcb 100644 --- a/include/libg1m/format/std/fkey.h +++ b/include/libg1m/format/std/fkey.h @@ -52,17 +52,16 @@ struct g1n_subheader { /* ************************************************************************** */ /* G3L-N - Function-keys files for CG calculators */ /* ************************************************************************** */ -/* G3L-N have exactly the same header and subheader as G3Ls, except - * the magic field is "0201" and not "0401" like for "normal" G3Ls. - * - * TODO: it is unknown yet how to identify G3N files using the StandardHeader, - * as no non-community-made one of them was found and the calculator software - * just skips StandardHeader. It has to be found for a correct integration - * in libg1m! - * - * In G3L-Ns, function keys are 64x24 1-bit images. */ +/* G3N (also named G3L-N) have exactly the same structure than G3Ls (see + * `libg1m/format/std/lang.h`), but messages are 64x24 1-bit pictures. */ # define FKEY3_WIDTH 64 # define FKEY3_HEIGHT 24 + +/* TODO: it is unknown yet how to identify G3N/G3L files using the + * StandardHeader, as no 'official' one of them was found and the calculator + * software just skips StandardHeader. It should be found for a better + * integration in libg1m! */ + # pragma pack() #endif /* LIBG1M_FORMAT_STD_FKEY_H */ diff --git a/include/libg1m/format/std/lang.h b/include/libg1m/format/std/lang.h index ec0303a..eac7970 100644 --- a/include/libg1m/format/std/lang.h +++ b/include/libg1m/format/std/lang.h @@ -52,66 +52,9 @@ struct g1l_subheader { /* ************************************************************************** */ /* G3L - Language files for Prizm */ /* ************************************************************************** */ -/* Thanks to amazonka for his (minimalist) description of the G3L format - * at Cemetech. So the G3L format starts off with a header: */ - -struct g3l_subheader { - /* a checksum */ - uint32_t checksum; - - /* magic: - * - 0x04, 0x01, 0x00, 0x00 for language files; - * - 0x02, 0x01, 0x00, 0x00 for function key files. */ - uint8_t magic[4]; - - /* lol */ - uint8_t undocumented[14]; - - /* size of the message zone size - unreliable (valid could have zero) */ - uint32_t message_zone_size; - - /* undocumented, again */ - uint8_t undocumented5[6]; - - /* name of the language */ - uint8_t language_name2[28]; - - /* size of the entire file */ - uint32_t filesize; - - /* size of the entire file - starts with an '@' */ - uint8_t internal_name[8]; - - /* undocumented */ - uint8_t undocumented2[200]; - - /* version number string: "XX.XX.XXXX" */ - uint8_t version[10]; - - /* unused */ - uint8_t unused2[2]; - - /* creation time: "YYYY.MMDD.HHMM" */ - uint8_t datetime[14]; - - /* big undocumented thing */ - uint8_t undocumented3[3410]; - - /* language name (zero terminated) */ - uint8_t language_name[16]; - - /* language salutation (zero terminated) */ - uint8_t language_salutation[16]; - - /* filename (extension included) */ - uint8_t g3l_filename[16]; - - /* undocumented */ - uint8_t undocumented4[308]; -}; - -/* Then there is something amazonka names the "executable code section". - * This is in fact the message zone. */ +/* G3L and G3N start with the StandardHeader and the Standard sub-Header, + * then the Prizm-specific subheader. After this, both the G3L and G3N + * have this language header: */ struct g3l_lang_header { /* sequence: '4C 59 37 35 35 00 00 00 02' (LY755 ) */ @@ -130,14 +73,7 @@ struct g3l_lang_header { /* Then we have offsets of all messages (4 bytes each), * then messages themselves, zero-terminated. * - * The four last bytes of the files are a little footer, which is: */ - -struct g3l_footer { - /* copy of the first checksum in the subheader */ - uint32_t checksum; -}; - -/* This footer is not counted as part of the file. */ + * And don't forget the footer (see `libg1m/format/std.h`) */ # pragma pack() #endif /* LIBG1M_FORMAT_STD_LANG_H */ diff --git a/include/libg1m/formatutils.h b/include/libg1m/formatutils.h index 5d17941..70447c6 100644 --- a/include/libg1m/formatutils.h +++ b/include/libg1m/formatutils.h @@ -35,11 +35,18 @@ extern "C" { /* General types (`g1m_t`) */ /* ************************************************************************** */ /* Standard header */ +# define g1m_stdflag_check1 0x0001 /* check first control value */ +# define g1m_stdflag_check2 0x0002 /* check second control value */ +# define g1m_stdflag_sub 0x0004 /* is followed by a prizm_subheader */ extern int g1m_maketype_std(const char *path, unsigned char *main_id, unsigned char *subtype, - const char **info, int *check_one, int *check_two, - unsigned int *platform, g1m_type_t *type); + const char **info, unsigned int *flags, + g1m_platform_t *platform, g1m_type_t *type); +/* Standard Subheader */ +//# define g1m_subflag_check 0x0001 +extern int g1m_maketype_sub(int raw_type, int raw_pf, unsigned int *flags, + g1m_type_t *type, g1m_platform_t *platform); /* ************************************************************************** */ /* MCS types (`g1m_mcshead_t`/`g1m_mcsfile_t`) */ /* ************************************************************************** */ diff --git a/include/libg1m/handle.h b/include/libg1m/handle.h index 4d5ceda..a94afed 100644 --- a/include/libg1m/handle.h +++ b/include/libg1m/handle.h @@ -71,7 +71,7 @@ typedef struct { /* Add-in related data */ char title[17]; - char internal_name[12]; + char intname[12]; g1m_version_t version; time_t creation_date; @@ -93,7 +93,9 @@ typedef struct { /* Picture-related data (also used for add-in icons */ int width, height; - uint32_t **pixels; /* 0x0RGB */ + uint32_t **pixels; /* 0x0RGB */ + uint32_t **icon_unsel; /* 0x0RGB */ + uint32_t **icon_sel; /* 0x0RGB */ /* E-activities related data */ g1m_line_t *line; diff --git a/include/libg1m/internals.h b/include/libg1m/internals.h index a68892e..9b91e6a 100644 --- a/include/libg1m/internals.h +++ b/include/libg1m/internals.h @@ -85,8 +85,10 @@ /* skip */ # define SKIP(SZ) { \ int SKIP_err = g1m_skip(buffer, SZ, NULL); \ - if (SKIP_err) return (SKIP_err); \ -} + if (SKIP_err) return (SKIP_err); } +# define GSKIP(SZ) \ + err = g1m_skip(buffer, SZ, NULL); \ + if (err) goto fail; /* write */ # define WRITE(BUF, SZ) \ @@ -97,42 +99,52 @@ /* Decoding functions */ /* ************************************************************************** */ /* with expected types */ -int g1m_decode_std(g1m_t *handle, const char *path, g1m_buffer_t *buffer, +int g1m_decode_std(g1m_t **handle, const char *path, g1m_buffer_t *buffer, struct standard_header*, g1m_type_t expected_types); /* w/o expected types */ -int g1m_decode_casemul(g1m_t *handle, g1m_buffer_t *buffer); -int g1m_decode_storage(g1m_t *handle, g1m_buffer_t *buffer); -int g1m_decode_cas(g1m_t *handle, g1m_buffer_t *buffer); -int g1m_decode_grc(g1m_t *handle, g1m_buffer_t *buffer); +int g1m_decode_casemul(g1m_t **handle, g1m_buffer_t *buffer, int big_endian); +int g1m_decode_storage(g1m_t **handle, g1m_buffer_t *buffer); +int g1m_decode_cas(g1m_t **handle, g1m_buffer_t *buffer); +int g1m_decode_grc(g1m_t **handle, g1m_buffer_t *buffer); /* ************************************************************************** */ /* "Std"-specific decoding functions */ /* ************************************************************************** */ # define G1M_STDFUNC(NAME) \ -int g1m_decode_std_##NAME(g1m_t *handle, g1m_buffer_t *buffer, \ +extern int g1m_decode_std_##NAME(g1m_t **handle, g1m_buffer_t *buffer, \ struct standard_header *std); +# define G1M_PRIZMFUNC(NAME) \ +extern int g1m_decode_std_cg_##NAME(g1m_t **handle, g1m_buffer_t *buffer, \ + struct standard_header *std, struct standard_subheader *sub, \ + struct _prizm_subheader *pzm, uint32_t *check); +# define G1M_CPFUNC(NAME) \ +extern int g1m_decode_std_cp_##NAME(g1m_t **handle, g1m_buffer_t *buffer, \ + struct standard_header *std, struct standard_subheader *sub, \ + struct _classpad_subheader *cp, uint32_t *check); +/* standard funcs */ G1M_STDFUNC(g3p) G1M_STDFUNC(c2p) G1M_STDFUNC(mcs) G1M_STDFUNC(eact) G1M_STDFUNC(addin) -G1M_STDFUNC(addin_cg) G1M_STDFUNC(lang) -G1M_STDFUNC(lang_cg) G1M_STDFUNC(fkey) -/* others */ -int g1m_decode_fkey_cg_content(g1m_t *handle, g1m_buffer_t *buffer, - uint_fast32_t zonesize, uint32_t *pchecksum); +/* specific funcs */ +G1M_CPFUNC(addin) +G1M_PRIZMFUNC(addin) +G1M_PRIZMFUNC(lang) +G1M_PRIZMFUNC(fkey) /* ************************************************************************** */ /* MCS-specific decoding functions */ /* ************************************************************************** */ # define G1M_MCSFUNC(NAME) \ -int g1m_decode_mcs_##NAME(g1m_mcsfile_t **handle, g1m_buffer_t *buffer, \ +extern int g1m_decode_mcs_##NAME(g1m_mcsfile_t **handle, g1m_buffer_t *buffer, \ g1m_mcshead_t *head); \ -int g1m_announce_mcs_##NAME(const g1m_mcsfile_t *handle, size_t *sz); \ -int g1m_encode_mcs_##NAME(const g1m_mcsfile_t *handle, g1m_buffer_t *buffer); +extern int g1m_announce_mcs_##NAME(const g1m_mcsfile_t *handle, size_t *sz); \ +extern int g1m_encode_mcs_##NAME(const g1m_mcsfile_t *handle, \ + g1m_buffer_t *buffer); G1M_MCSFUNC(var) G1M_MCSFUNC(list) @@ -172,12 +184,10 @@ G1M_CASFUNC(capture) /* Utilities */ /* ************************************************************************** */ /* Free-ing */ -void g1m_free_content(g1m_t *handle); -void g1m_free_mcs(g1m_t *handle); void g1m_free_line_content(g1m_line_t *line); /* Skipping */ -int g1m_skip(g1m_buffer_t *buffer, size_t size, uint_fast32_t *checksum); +int g1m_skip(g1m_buffer_t *buffer, size_t size, uint32_t *checksum); /* Checksum-ing */ uint8_t g1m_checksum8(void *mem, size_t size); diff --git a/src/core/open.c b/src/core/open.c index 313c47f..b58f9bc 100644 --- a/src/core/open.c +++ b/src/core/open.c @@ -47,7 +47,7 @@ int g1m_open(g1m_t **handle, const char *path, /** * g1m_fopen: - * Open handle using FILE* pointer. + * Open handle using pointer on FILE. * * Will not seek, and will not keep the stream. * Make sure to fclose the stream after use. @@ -62,31 +62,18 @@ int g1m_open(g1m_t **handle, const char *path, int g1m_fopen(g1m_t **handle, const char *path, FILE *stream, g1m_type_t expected) { - int err; - /* check stream */ if (!stream) return (g1m_error_nostream); if (!__freadable(stream)) return (g1m_error_noread); - /* create the handle */ - *handle = malloc(sizeof(g1m_t)); - if (!*handle) return (g1m_error_alloc); - /* make the buffer */ g1m_buffer_t buffer = { .cookie = stream, .read = g1m_filebuffer_read }; - /* fill it by parsing opened file */ - if ((err = g1m_decode(*handle, path, &buffer, expected))) { - free(*handle); - *handle = NULL; - return (err); - } - - /* everything ok */ - return (0); + /* decode opened file */ + return (g1m_decode(handle, path, &buffer, expected)); } #endif diff --git a/src/decode/cas.c b/src/decode/cas.c index 5ecb8bc..25567f3 100644 --- a/src/decode/cas.c +++ b/src/decode/cas.c @@ -235,24 +235,22 @@ int g1m_decode_casfile_part(g1m_mcsfile_t *file, g1m_buffer_t *buffer) /* ************************************************************************** */ /** * g1m_decode_cas: - * Decode a CAS file. TODO. + * Decode a CAS file. * * Is also sort of a guide for using the CAS MCS files. * The colon ':' (0x3A) is already read at this point (CAS file id.). * - * @arg handle the handle to create. - * @arg buffer the buffer to read from. - * @return the libg1m error. + * @arg h the handle to create. + * @arg buffer the buffer to read from. + * @return the libg1m error. */ -int g1m_decode_cas(g1m_t *handle, g1m_buffer_t *buffer) +int g1m_decode_cas(g1m_t **h, g1m_buffer_t *buffer) { - int err; - - /* prepare the handle */ - handle->type = g1m_type_mcs; - handle->files = NULL; - handle->_size = 0; + /* make the handle */ + int err = g1m_make_mcs(h, 0); + if (err) return (err); + g1m_t *handle = *h; /* read each */ for (handle->count = 0;;) { @@ -308,7 +306,7 @@ int g1m_decode_cas(g1m_t *handle, g1m_buffer_t *buffer) fail: /* END OF DA WORLD */ - g1m_free_mcs(handle); + g1m_free(handle); return (err); } @@ -316,12 +314,12 @@ fail: * g1m_decode_grc: * Decode Graph Card file. * - * @arg handle the handle. + * @arg handle the handle to make. * @arg buffer the buffer to read from. * @return the error code (0 if ok). */ -int g1m_decode_grc(g1m_t *handle, g1m_buffer_t *buffer) +int g1m_decode_grc(g1m_t **handle, g1m_buffer_t *buffer) { uint8_t intro[2]; READ(intro, 2) /* probably the file count? */ uint8_t colon; READ(&colon, 1) /* read first colon */ diff --git a/src/decode/casemul.c b/src/decode/casemul.c index 23e8b97..7db66b5 100644 --- a/src/decode/casemul.c +++ b/src/decode/casemul.c @@ -298,14 +298,15 @@ static int read_list(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer, * g1m_decode_casemul: * Decode a CasEmul file. * - * @arg handle the handle. + * @arg h the handle to make. * @arg buffer the buffer to read from. + * @arg big_endian whether the file is big endian or not. * @return the error code (0 if ok). */ -int g1m_decode_casemul(g1m_t *handle, g1m_buffer_t *buffer) +int g1m_decode_casemul(g1m_t **h, g1m_buffer_t *buffer, int big_endian) { - int err, big_endian = handle->platform & g1m_platflag_be; + int err; /* read the overall header's internal header end */ struct casemul_internal_header overall_int = { @@ -331,15 +332,11 @@ int g1m_decode_casemul(g1m_t *handle, g1m_buffer_t *buffer) return (err); DREAD(src, casemul_source_header) - /* prepare the tab */ - handle->type = g1m_type_mcs; - handle->platform = g1m_platform_fx; - handle->count = 0; - handle->_size = /* src.programs + */ src.pictures + src.matrixes + - src.lists; - if (!(handle->files = malloc(sizeof(g1m_mcsfile_t*) * handle->_size))) - return (g1m_error_alloc); - memset(handle->files, 0, sizeof(g1m_mcsfile_t*) * handle->_size); + /* make the handle */ + err = g1m_make_mcs(h, /* src.programs + */ src.pictures + src.matrixes + + src.lists); + if (err) return (err); + g1m_t *handle = *h; /* read each program; TODO: put this in a function when token parsing * is managed. */ @@ -394,6 +391,6 @@ int g1m_decode_casemul(g1m_t *handle, g1m_buffer_t *buffer) handle->platform &= ~g1m_platflag_be; return (0); fail: - g1m_free_mcs(handle); + g1m_free(handle); return (err); } diff --git a/src/decode/main.c b/src/decode/main.c index cd68f64..7b1e0b3 100644 --- a/src/decode/main.c +++ b/src/decode/main.c @@ -22,7 +22,7 @@ /* Check using extensions */ /* ************************************************************************** */ /* Decode function */ -typedef int (*decode_func)(g1m_t*, g1m_buffer_t*); +typedef int (*decode_func)(g1m_t**, g1m_buffer_t*); /* Correspondance type */ struct corresp { @@ -90,7 +90,7 @@ static int lookup_extension(const char *path, g1m_type_t types, * @return the error code (0 if ok). */ -int g1m_decode(g1m_t *handle, const char *path, g1m_buffer_t *buffer, +int g1m_decode(g1m_t **handle, const char *path, g1m_buffer_t *buffer, g1m_type_t expected_types) { /* initialize the handle */ @@ -112,15 +112,11 @@ int g1m_decode(g1m_t *handle, const char *path, g1m_buffer_t *buffer, /* identify a Casemul file */ READ(&buf[1], 3) - if (!memcmp(buf, "CAFS", 4)) { - handle->platform |= g1m_platflag_be; + int casemul_be = !memcmp(buf, "CAFS", 4); + if (casemul_be || !memcmp(buf, "ACFS", 4)) { if (expected_types && !(expected_types & g1m_type_mcs)) return (g1m_error_wrong_type); - return (g1m_decode_casemul(handle, buffer)); - } else if (!memcmp(buf, "ACFS", 4)) { - if (expected_types && !(expected_types & g1m_type_mcs)) - return (g1m_error_wrong_type); - return (g1m_decode_casemul(handle, buffer)); + return (g1m_decode_casemul(handle, buffer, casemul_be)); } /* identify a standard header (send a _copy_) */ diff --git a/src/decode/std.c b/src/decode/std.c index 1b073dc..ce764cf 100644 --- a/src/decode/std.c +++ b/src/decode/std.c @@ -23,7 +23,7 @@ /* Getting the parsing function */ /* ************************************************************************** */ /* Correspondance type */ -typedef int (*decode_func)(g1m_t*, g1m_buffer_t*, struct standard_header*); +typedef int (*decode_func)(); struct corresp { unsigned int platform; unsigned int type; @@ -36,23 +36,22 @@ struct corresp { static struct corresp parsing_functions[] = { /* add-ins */ {g1m_platform_fx, g1m_type_addin, FUNC(addin)}, - {g1m_platform_cg, g1m_type_addin, FUNC(addin_cg)}, + {g1m_platform_cp, g1m_type_addin, FUNC(cp_addin)}, + {g1m_platform_cg, g1m_type_addin, FUNC(cg_addin)}, /* mcs */ {g1m_platform_fx, g1m_type_mcs, FUNC(mcs)}, {g1m_platform_cg, g1m_type_mcs, FUNC(mcs)}, /* language files */ {g1m_platform_fx, g1m_type_lang, FUNC(lang)}, - {g1m_platform_cg, g1m_type_lang, FUNC(lang_cg)}, + {g1m_platform_cg, g1m_type_lang, FUNC(cg_lang)}, /* function keys file */ {g1m_platform_fx, g1m_type_fkey, FUNC(fkey)}, - {g1m_platform_cg, g1m_type_fkey, FUNC(lang_cg)}, + {g1m_platform_cg, g1m_type_fkey, FUNC(cg_fkey)}, /* e-activities */ {g1m_platform_fx, g1m_type_eact, FUNC(eact)}, /* pictures */ {g1m_platform_cg, g1m_type_pict, FUNC(g3p)}, {g1m_platform_cp, g1m_type_pict, FUNC(c2p)}, - /* storage */ -// {g1m_platform_none, g1m_type_storage, FUNC(storage)}, {0, 0, NULL} }; @@ -61,37 +60,18 @@ static struct corresp parsing_functions[] = { * find_decode_function: * Find the parsing function. * - * @arg handle the handle. - * @arg path the file path. - * @arg std pointer to the standard header. + * @arg platform the platform. + * @arg type the type. * @arg rd pointer to the decode function. * @return the error code (0 if ok). */ -static int find_decode_function(g1m_t *handle, const char *path, - struct standard_header *std, decode_func *rd) +static int find_decode_function(g1m_platform_t platform, g1m_type_t type, + decode_func *rd) { - /* get the type */ - unsigned int platform; - g1m_type_t type; - const char *type_string; - int check_one, check_two; - if (g1m_maketype_std(path, std->main_id, std->subtype, &type_string, - &check_one, &check_two, &platform, &type)) - return (g1m_error_magic); - log_info("Standard Header type is '%s'.", type_string); - - /* check control bytes */ - if (check_one && std->control != ((std->filesize + 0x41) & 0xff)) { - log_info("First control byte isn't right."); - return (g1m_error_magic); - } else if (check_two && std->control2 != ((std->filesize + 0xb8) & 0xff)) { - log_info("Second control byte isn't right."); - return (g1m_error_magic); - } - - /* look for corresponding parsing function */ + /* get the function */ struct corresp *c = parsing_functions - 1; + log_info("Type is %04X, platform is %04X", type, platform); while ((++c)->decode) { if (c->type != type) continue; @@ -106,8 +86,6 @@ static int find_decode_function(g1m_t *handle, const char *path, } /* set the vars */ - handle->type = type; - handle->platform = platform; *rd = c->decode; return (0); } @@ -119,7 +97,7 @@ static int find_decode_function(g1m_t *handle, const char *path, * g1m_decode_std: * Decode a file with standard header. * - * @arg handle the handle. + * @arg handle the handle to create. * @arg path the path. * @arg buffer the buffer to read from. * @arg std the standard header. @@ -127,29 +105,97 @@ static int find_decode_function(g1m_t *handle, const char *path, * @return the error code (0 if ok). */ -int g1m_decode_std(g1m_t *handle, const char *path, g1m_buffer_t *buffer, +int g1m_decode_std(g1m_t **handle, const char *path, g1m_buffer_t *buffer, struct standard_header *std, g1m_type_t expected_types) { + int err; /* reverse the standard header */ uint8_t *u = (uint8_t*)std; + uint32_t check = g1m_checksum32(std, sizeof(struct standard_header), 0); for (size_t i = 0; i < sizeof(struct standard_header); i++) u[i] = ~u[i]; /* print header */ log_info("Raw inverted standard header is:"); logm_info(std, sizeof(struct standard_header)); - - /* correct standard header endianess */ std->filesize = be32toh(std->filesize); std->number = be16toh(std->number); - /* get type */ - decode_func read_func; int err; - if ((err = find_decode_function(handle, path, std, &read_func))) - return (err); + /* get the type */ + g1m_platform_t platform; g1m_type_t type; + const char *type_string; unsigned int mflags; + decode_func rd; + if (g1m_maketype_std(path, std->main_id, std->subtype, &type_string, + &mflags, &platform, &type)) + return (g1m_error_magic); + log_info("Standard Header type is '%s'.", type_string); - /* check if the type is alright */ - if (expected_types && handle->type != expected_types) + /* check control bytes */ + if (mflags & g1m_stdflag_check1 + && std->control != ((std->filesize + 0x41) & 0xff)) { + log_info("First control byte isn't right."); + return (g1m_error_magic); + } else if (mflags & g1m_stdflag_check2 + && std->control2 != ((std->filesize + 0xb8) & 0xff)) { + log_info("Second control byte isn't right."); + return (g1m_error_magic); + } + + /* check if there is a standard subheader */ + if (mflags & g1m_stdflag_sub) { + log_info("Has a Standard Subheader!"); + DREAD(hd, standard_subheader) + if (g1m_maketype_sub(hd.filetype, hd.platform, + &mflags, &type, &platform)) + return (g1m_error_magic); + + /* TODO: controls */ + /* find the decode function */ + if (find_decode_function(platform, type, &rd)) + return (g1m_error_magic); + if (expected_types && !(type & expected_types)) + return (g1m_error_wrong_type); + + /* read and decode for specific platforms */ + check = g1m_checksum32(&hd.filetype, + sizeof(struct standard_subheader) - 4, check); + if (platform == g1m_platform_cp) { + DREAD(shd, _classpad_subheader) + check = g1m_checksum32(&shd, sizeof(struct _classpad_subheader), + check); + + /* decode the file content */ + err = (*rd)(handle, buffer, std, &hd, &shd, &check); + if (err) return (err); + } else if (platform == g1m_platform_cg) { + DREAD(shd, _prizm_subheader) + check = g1m_checksum32(&shd, sizeof(struct _prizm_subheader), + check); + + /* decode the file content */ + err = (*rd)(handle, buffer, std, &hd, &shd, &check); + if (err) return (err); + + /* read the footer */ + uint32_t endcheck; GREAD(&endcheck, sizeof(uint32_t)) + err = g1m_error_checksum; + if (be32toh(endcheck) != be32toh(hd.checksum)) + goto fail; + } + + /* check the sum */ + err = g1m_error_checksum; + if (check != be32toh(hd.checksum)) + goto fail; + + /* no error */ + return (0); + } + + /* find the decode function */ + if (find_decode_function(platform, type, &rd)) + return (g1m_error_magic); + if (expected_types && !(type & expected_types)) return (g1m_error_wrong_type); /* log some data */ @@ -157,5 +203,8 @@ int g1m_decode_std(g1m_t *handle, const char *path, g1m_buffer_t *buffer, log_info("Standard Header num is %" PRIu16 ".", std->number); /* subdecode. */ - return ((*read_func)(handle, buffer, std)); + return ((*rd)(handle, buffer, std)); +fail: + g1m_free(*handle); *handle = NULL; + return (err); } diff --git a/src/decode/std/addin.c b/src/decode/std/addin.c index df656e5..0094fe4 100644 --- a/src/decode/std/addin.c +++ b/src/decode/std/addin.c @@ -18,71 +18,19 @@ * ************************************************************************** */ #include -/** - * addin_set_data: - * Set handle data. - * - * Will take basic add-in data, put it into the right interface format - * and store it. - * - * @arg handle the handle. - * @arg title the addin title. - * @arg title_size the addin title size. - * @arg internal_name the addin internal name. - * @arg internal_name_size the addin internal name size. - * @arg version the version string. - * @arg creation_time the creation time. - */ - -static void addin_set_data(g1m_t *handle, - char *title, size_t title_size, - char *internal_name, size_t internal_name_size, - char *version, char *creation_time) -{ - /* useful vars */ - const int two = '0' + '0' * 10; - const int four = two + '0' * 100 + '0' * 1000; - - /* put title */ - bzero(handle->title, 17); - strncpy(handle->title, title, title_size); - - /* put internal name */ - bzero(handle->internal_name, 12); - strncpy(handle->internal_name, internal_name, internal_name_size); - - /* put version */ -#define v version - handle->version = (g1m_version_t){ - .major = v[0] * 10 + v[1] - two, - .minor = v[3] * 10 + v[4] - two, - .revision = v[6] * 1000 + v[7] * 100 + v[8] * 10 + v[9] - four - }; -#undef v - - /* put time */ -#define c creation_time - struct tm created = { - .tm_year = c[0] * 1000 + c[1] * 100 + c[2] * 10 + c[3] - four, - .tm_mon = c[5] * 10 + c[6] - two, - .tm_mday = c[7] * 10 + c[8] - two, - .tm_hour = c[10] * 10 + c[11] - two, - .tm_min = c[12] * 10 + c[13] - two - }; - handle->creation_date = mktime(&created); -#undef c -} - +/* ************************************************************************** */ +/* Add-in for the fx-9860G (G1A) */ +/* ************************************************************************** */ /** * g1m_decode_std_addin: * Decodes a "normal" add-in (after Standard Header). * - * @arg handle the handle. + * @arg h the handle to make. * @arg buffer the buffer to read from. * @return the error code (0 if ok). */ -int g1m_decode_std_addin(g1m_t *handle, g1m_buffer_t *buffer, +int g1m_decode_std_addin(g1m_t **h, g1m_buffer_t *buffer, struct standard_header *std) { int err = 0; @@ -90,33 +38,34 @@ int g1m_decode_std_addin(g1m_t *handle, g1m_buffer_t *buffer, /* get the subheader */ DREAD(hd, g1a_subheader) - - /* correct subheader endianess */ hd.filesize = be32toh(hd.filesize); + /* make the handle */ + g1m_version_t version; g1m_decode_version((char*)hd.version, &version); + time_t created; g1m_decode_date((char*)hd.creation_date, &created); + err = g1m_make_addin(h, g1m_platform_fx, &version, &created); + if (err) return (err); + g1m_t *handle = *h; + + /* set more data */ + strncpy(handle->title, (char*)hd.title, 8); + handle->title[8] = 0; + strncpy(handle->intname, (char*)hd.internal_name, 8); + handle->intname[8] = 0; + /* log info about the subheader */ - log_info("internal name is '%.8s'", hd.internal_name); - log_info("title is '%.8s'", hd.title); + log_info("title is '%s'", handle->title); + log_info("internal name is '%s'", handle->intname); log_info("estrips count is %" PRIu8, hd.estrips_count); - log_info("version is %.10s", hd.version); - log_info("creation date is %.14s", hd.creation_date); - - /* store data in the handle */ - addin_set_data(handle, - (char*)hd.title, 8, - (char*)hd.internal_name, 8, - (char*)hd.version, (char*)hd.creation_date); - - /* allocate space for icon */ - handle->width = G1A_ICON_WIDTH; - handle->height = G1A_ICON_HEIGHT; - handle->pixels = alloc_pixels(G1A_ICON_WIDTH, G1A_ICON_HEIGHT); - if (!handle->pixels) return (g1m_error_alloc); - prepare_pixels(handle->pixels, G1A_ICON_WIDTH, G1A_ICON_HEIGHT) + log_info("version is %02u.%02u.%04u", handle->version.major, + handle->version.minor, handle->version.revision); + log_info("creation date is: %.24s", ctime(&handle->creation_date)); /* fill icon */ - g1m_decode_picture(handle->pixels, g1m_pictureformat_1bit_packed, - hd.icon, G1A_ICON_WIDTH, G1A_ICON_HEIGHT); + g1m_decode_picture(handle->icon_unsel, g1m_pictureformat_1bit_packed, + hd.icon, handle->width, handle->height); + g1m_decode_picture(handle->icon_sel, g1m_pictureformat_1bit_packed, + hd.icon, handle->width, handle->height); /* skip size */ SKIP(hd.filesize - sizeof(struct standard_header) @@ -126,98 +75,130 @@ int g1m_decode_std_addin(g1m_t *handle, g1m_buffer_t *buffer, return (err); } +/* ************************************************************************** */ +/* Add-in for the fx-CP/Classpad (C1A) */ +/* ************************************************************************** */ /** - * g1m_decode_std_addin_cg: - * Decode fx-CG add-in (after Standard Header). + * g1m_decode_std_cp_addin: + * Decode fx-CP add-in. * - * @arg handle the handle. + * @arg h the handle to make. * @arg buffer the buffer to read from. * @arg std the standard header. + * @arg sub the standard subheader. + * @arg cp the classpad-specific subheader. + * @arg check the checksum to feed. * @return the error code (0 if ok). */ -int g1m_decode_std_addin_cg(g1m_t *handle, g1m_buffer_t *buffer, - struct standard_header *std) +int g1m_decode_std_cp_addin(g1m_t **h, g1m_buffer_t *buffer, + struct standard_header *std, struct standard_subheader *sub, + struct _classpad_subheader *cp, uint32_t *check) { int err = 0; - /* get the subheader */ - DREAD(hd, addin_cp_subheader) - /* correct subheader endianness */ - hd.checksum = be32toh(hd.checksum); - hd.control = be32toh(hd.control); - hd.filesize = be32toh(hd.filesize); + /* read the add-in subheader */ + DREAD(cphd, c1a_subheader) + *check = g1m_checksum32(&cphd, sizeof(struct c1a_subheader), *check); - /* control byte */ - size_t content_size = hd.filesize - 4 - - hd.magic[1] ? 0x7000 : 0x1000; - if (hd.control != content_size) { - log_fatal("control value is incorrect!"); - log_fatal("calculated %" PRIuSIZE ", expected %" PRIu32, - content_size, hd.control); - return (g1m_error_magic); - } + /* make the handle */ + g1m_version_t version; g1m_decode_version((char*)sub->version, &version); + time_t created; g1m_decode_date((char*)sub->timestamp, &created); + err = g1m_make_addin(h, g1m_platform_cp, &version, &created); + if (err) return (err); + g1m_t *handle = *h; - /* start making checksum */ - uint_fast32_t checksum = - sizeof(struct standard_header) * 256 - - g1m_checksum32(std, sizeof(struct standard_header), 0) - + g1m_checksum32(&hd.magic, sizeof(struct addin_cp_subheader), 0); + /* copy other basic information */ + strncpy(handle->intname, (char*)sub->internal_name, 8); + handle->intname[9] = 0; + strncpy(handle->title, (char*)sub->title, 16); + handle->title[16] = 0; - /* read subheader */ - if (hd.magic[1]) { /* is Prizm */ - DREAD(shd, g3a_subheader) - - checksum += g1m_checksum32(&shd, sizeof(struct g3a_subheader), 0); - handle->pixels = alloc_pixels(G3A_ICON_WIDTH, G3A_ICON_HEIGHT); - handle->width = G3A_ICON_WIDTH; handle->height = G3A_ICON_HEIGHT; - if (!handle->pixels) return (g1m_error_alloc); - prepare_pixels(handle->pixels, G3A_ICON_WIDTH, G3A_ICON_HEIGHT) - - g1m_decode_picture(handle->pixels, g1m_pictureformat_16bit, - shd.unselected_icon_image, G3A_ICON_WIDTH, G3A_ICON_HEIGHT); - } else { /* is C1A */ - DREAD(shd, c1a_subheader) - - checksum += g1m_checksum32(&shd, sizeof(struct c1a_subheader), 0); - handle->pixels = alloc_pixels(C1A_ICON_WIDTH, C1A_ICON_HEIGHT); - handle->width = C1A_ICON_WIDTH; handle->height = C1A_ICON_HEIGHT; - if (!handle->pixels) return (g1m_error_alloc); - prepare_pixels(handle->pixels, C1A_ICON_WIDTH, C1A_ICON_HEIGHT); - - g1m_decode_picture(handle->pixels, g1m_pictureformat_1bit_packed, - shd.icon, C1A_ICON_WIDTH, C1A_ICON_HEIGHT); - } - - /* skip content for now */ - if ((err = g1m_skip(buffer, content_size, &checksum))) - goto fail; - - /* check the sum (yo!) */ - err = g1m_error_magic; - if (checksum != hd.checksum) { - log_fatal("Invalid checksum!"); - log_fatal("Header checksum is %" PRIu32 - ", calculated checksum is %" PRIuFAST32, - hd.checksum, checksum); - goto fail; - } + /* decode pictures */ + g1m_decode_picture(handle->icon_unsel, g1m_pictureformat_1bit_packed, + cphd.icon, handle->width, handle->height); + g1m_decode_picture(handle->icon_sel, g1m_pictureformat_1bit_packed, + cphd.icon, handle->width, handle->height); /* log */ - log_info("title is '%.16s'", hd.title); - log_info("internal name is '%.11s'", hd.internal_name); - log_info("version is '%.10s'", hd.version); - log_info("timestamp is '%.14s'", hd.stamp); + log_info("title is '%s'", handle->title); + log_info("internal name is '%s'", handle->intname); + log_info("version is %02u.%02u.%04u", handle->version.major, + handle->version.minor, handle->version.revision); + log_info("timestamp is %.24s", ctime(&handle->creation_date)); - /* store basic data in the handle */ - addin_set_data(handle, - (char*)hd.title, 16, - (char*)hd.internal_name, 12, - (char*)hd.version, (char*)hd.stamp); + /* skip content for now */ + size_t content_size = be32toh(sub->filesize) - 0x1000; + if ((err = g1m_skip(buffer, content_size, check))) + goto fail; /* no error */ return (0); fail: - free(handle->pixels); + g1m_free(*h); *h = NULL; + return (err); +} + +/* ************************************************************************** */ +/* Add-in for the fx-CG/Prizm (G3A) */ +/* ************************************************************************** */ +/** + * g1m_decode_std_cg_addin: + * Decode fx-CG add-in. + * + * @arg h the handle to make. + * @arg buffer the buffer to read from. + * @arg std the standard header. + * @arg sub the standard subheader. + * @arg prizm the prizm-specific subheader. + * @arg check the checksum to feed. + * @return the error code (0 if ok). + */ + +int g1m_decode_std_cg_addin(g1m_t **h, g1m_buffer_t *buffer, + struct standard_header *std, struct standard_subheader *sub, + struct _prizm_subheader *prizm, uint32_t *check) +{ + int err = 0; + + /* read the add-in subheader */ + DREAD(cghd, g3a_subheader) + *check = g1m_checksum32(&cghd, sizeof(struct g3a_subheader), *check); + + /* make the handle */ + g1m_version_t version; g1m_decode_version((char*)sub->version, &version); + time_t created; g1m_decode_date((char*)sub->timestamp, &created); + err = g1m_make_addin(h, g1m_platform_cg, &version, &created); + if (err) return (err); + g1m_t *handle = *h; + + /* copy other basic information */ + strncpy(handle->intname, (char*)sub->internal_name, 8); + handle->intname[9] = 0; + strncpy(handle->title, (char*)sub->title, 16); + handle->title[16] = 0; + + /* decode pictures */ + g1m_decode_picture(handle->icon_unsel, g1m_pictureformat_16bit, + cghd.unselected_icon_image, handle->width, handle->height); + g1m_decode_picture(handle->icon_sel, g1m_pictureformat_16bit, + cghd.selected_icon_image, handle->width, handle->height); + + /* log */ + log_info("title is '%s'", handle->title); + log_info("internal name is '%s'", handle->intname); + log_info("version is %02u.%02u.%04u", handle->version.major, + handle->version.minor, handle->version.revision); + log_info("timestamp is %.24s", ctime(&handle->creation_date)); + + /* skip content for now */ + size_t content_size = be32toh(sub->filesize) - 4 - 0x7000; + if ((err = g1m_skip(buffer, content_size, check))) + goto fail; + + /* no error */ + return (0); +fail: + g1m_free(*h); *h = NULL; return (err); } diff --git a/src/decode/std/eact.c b/src/decode/std/eact.c index b756fbe..f63f2c9 100644 --- a/src/decode/std/eact.c +++ b/src/decode/std/eact.c @@ -427,19 +427,17 @@ static int eact_decode_line(g1m_line_t *handle, uint8_t *buf, size_t size, * * Thanks to Julese50 for his help on e-acts parsing. * - * @arg handle the handle. + * @arg h the handle to create. * @arg buffer the buffer to read from. * @return the error code (0 if ok). */ -int g1m_decode_std_eact(g1m_t * handle, g1m_buffer_t *buffer, +int g1m_decode_std_eact(g1m_t **h, g1m_buffer_t *buffer, struct standard_header *std) { - (void)std; + (void)std; int err; /* decode 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); @@ -452,22 +450,31 @@ int g1m_decode_std_eact(g1m_t * handle, g1m_buffer_t *buffer, 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; + /* allocate handle */ + *h = malloc(sizeof(g1m_t)); + if (!*h) return (g1m_error_alloc); + g1m_t *handle = *h; + memset(handle, 0, sizeof(g1m_t)); /* skip the setup area */ SKIP(hd.setup_area_size) + /* prepare the handle */ + handle->type = g1m_type_eact; + handle->platform = g1m_platform_fx; + if (hd.eact_version == EACT_G3E) /* check if fx-CG */ + handle->platform = g1m_platform_cg; + /* 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; + GREAD(&buf, bufsize) /* decode content */ + handle->line = &handle->_linedata; return (eact_decode_line_content(handle->line, buf, bufsize)); +fail: + g1m_free(*h); *h = NULL; + return (err); } diff --git a/src/decode/std/fkey.c b/src/decode/std/fkey.c index 950536e..a85e935 100644 --- a/src/decode/std/fkey.c +++ b/src/decode/std/fkey.c @@ -60,25 +60,22 @@ static uint32_t **fkeydup3(uint8_t *fkey) } /* ************************************************************************** */ -/* fx function keys file parsing utilities */ +/* Decoding fx function keys file (G1N) */ /* ************************************************************************** */ /** * g1m_decode_std_fkey: * Decode fx function keys files. * - * @arg handle the libg1m handle. + * @arg h the libg1m handle to create. * @arg buffer the buffer to read from. * @arg std pointer to the standard header. * @return the error code (0 if ok). */ -int g1m_decode_std_fkey(g1m_t *handle, g1m_buffer_t *buffer, +int g1m_decode_std_fkey(g1m_t **h, g1m_buffer_t *buffer, struct standard_header *std) { - (void)std; - /* set handle type */ - handle->type = g1m_type_fkey; - + int err; (void)std; /* read the subheader */ DREAD(hd, g1n_subheader) uint_fast16_t num = be16toh(hd.fkey_count) + 1; @@ -89,11 +86,10 @@ int g1m_decode_std_fkey(g1m_t *handle, g1m_buffer_t *buffer, uint8_t data[data_size]; READ(data, data_size) - /* allocate tab */ - handle->fkeys = malloc(sizeof(uint32_t**) * num); - handle->count = 0; - handle->_size = num; - if (!handle->fkeys) return (g1m_error_alloc); + /* prepare the handle */ + err = g1m_make_fkey(h, g1m_platform_fx, num); + if (err) return (err); + g1m_t *handle = *h; /* get the offset table */ uint16_t *offsets = (uint16_t*)data; @@ -104,20 +100,15 @@ int g1m_decode_std_fkey(g1m_t *handle, g1m_buffer_t *buffer, strncpy(handle->title, (char*)fkeys, 16); /* read all */ - for (uint_fast32_t i = 1; i < num; i++) { - handle->fkeys[i] = NULL; - if (offsets[i] == (uint16_t)-1) { - handle->count++; + for (handle->count = 0; handle->count < (int)num; handle->count++) { + int i = handle->count; + if (offsets[i] == (uint16_t)-1) continue ; - } - - /* correct offset */ offsets[i] = be16toh(offsets[i]); /* store */ handle->fkeys[i] = fkeydup(&fkeys[i]); if (!handle->fkeys[i]) goto fail; - handle->count++; } /* no error */ @@ -125,35 +116,76 @@ int g1m_decode_std_fkey(g1m_t *handle, g1m_buffer_t *buffer, /* omg fail! */ fail: - for (int i = 0; i < handle->count; i++) - free(handle->fkeys[i]); - free(handle->messages); - handle->messages = NULL; - return (g1m_error_alloc); + g1m_free(*h); *h = NULL; + return (err); } /* ************************************************************************** */ -/* cg function keys file parsing utilities */ +/* Decoding fx-CG/Prizm function keys file (G3N) */ /* ************************************************************************** */ /** - * g1m_decode_fkey_cg_content: + * g1m_decode_std_cg_fkey: * Decode fx-CG key files. * - * In fact, the main parsing function is `g1m_decode_lang_cg` (they really - * have the same subheader). TODO - * * @arg handle the libg1m handle. * @arg buffer the buffer to read from. + * @arg std the standard header. + * @arg sub the standard subheader. + * @arg prizm the prizm-specific subheader. + * @arg check the checksum to feed. * @return the error code (0 if ok). */ -int g1m_decode_fkey_cg_content(g1m_t *handle, g1m_buffer_t *buffer, - uint_fast32_t zonesize, uint32_t *pchecksum) +int g1m_decode_std_cg_fkey(g1m_t **h, g1m_buffer_t *buffer, + struct standard_header *std, struct standard_subheader *sub, + struct _prizm_subheader *prizm, uint32_t *check) { - handle->type = 0x00; - //handle->type = g1m_type_fkey; - (void)buffer; - (void)zonesize; - (void)pchecksum; - return (g1m_error_magic); + int err; + + /* read the subheader */ + DREAD(lhd, g3l_lang_header) + *check = g1m_checksum32(&lhd, sizeof(struct g3l_lang_header), *check); + + /* read the data */ + size_t data_size = sub->filesize - sizeof(struct standard_header) + - sizeof(struct standard_subheader) - sizeof(struct _prizm_subheader) + - sizeof(struct g3l_lang_header) - 4; + uint8_t data[data_size]; + READ(data, data_size) + *check = g1m_checksum32(data, data_size, *check); + + /* make the handle */ + int num = be32toh(lhd.num); + err = g1m_make_fkey(h, g1m_platform_cg, num); + if (err) return (err); + g1m_t *handle = *h; + + /* setup the pointers */ + uint32_t *offsets = (void*)data; + uint8_t *messages = (uint8_t*)&offsets[num + 1]; + + /* read messages */ + for (handle->count = 0; handle->count < (int)num; handle->count++) { + int i = handle->count; + if (offsets[i] == (uint32_t)-1) { + log_info("[#%d] -", i); + continue; + } + + /* correct offset and log */ + offsets[i] = be32toh(offsets[i]); + log_info("[#%d] '%s' (0x%" PRIu32 ")", i, + &messages[offsets[i]], offsets[i]); + + /* store */ + handle->fkeys[i] = fkeydup3((void*)&messages[offsets[i]]); + if (!handle->fkeys[i]) goto fail; + } + + /* done */ + return (0); + +fail: + g1m_free(*h); *h = NULL; + return (err); } diff --git a/src/decode/std/lang.c b/src/decode/std/lang.c index 50034eb..8391327 100644 --- a/src/decode/std/lang.c +++ b/src/decode/std/lang.c @@ -25,22 +25,18 @@ * g1m_decode_std_lang: * Decode fx language files. * - * @arg handle the libg1m handle. + * @arg h the libg1m handle to create. * @arg buffer the buffer to read from. * @return the error code (0 if ok). */ -int g1m_decode_std_lang(g1m_t *handle, g1m_buffer_t *buffer, +int g1m_decode_std_lang(g1m_t **h, g1m_buffer_t *buffer, struct standard_header *std) { - (void)std; - /* set handle type */ - handle->messages = NULL; + (void)std; int err; /* read the subheader */ DREAD(hd, g1l_subheader) - - /* correct the endianness */ uint_fast16_t num = be16toh(hd.message_count) + 1; /* log */ @@ -52,31 +48,26 @@ int g1m_decode_std_lang(g1m_t *handle, g1m_buffer_t *buffer, 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); + /* make the handle */ + err = g1m_make_lang(h, g1m_platform_fx, num); + if (err) return (err); + g1m_t *handle = *h; /* 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; + for (handle->count = 0; handle->count < (int)num; handle->count++) { + int i = handle->count; if (offsets[i] == (uint16_t)-1) { - log_info("[#%" PRIuFAST16 "] -", i); - handle->count++; - continue ; + log_info("[#%d] -", i); + continue; } - /* correct offset */ + /* correct offset and log */ offsets[i] = be16toh(offsets[i]); - - /* log */ - log_info("[#%" PRIuFAST16 "] '%s' (0x%" PRIu16 ")", i, + log_info("[#%d] '%s' (0x%" PRIu16 ")", i, &messages[offsets[i]], offsets[i]); /* store */ @@ -90,175 +81,76 @@ int g1m_decode_std_lang(g1m_t *handle, g1m_buffer_t *buffer, /* 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_decode_lang_cg_content: - * Decode the content of the message zone size for a G3L language file. - * - * @arg handle the libg1m handle. - * @arg buffer the buffer 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_decode_lang_cg_content(g1m_t *handle, g1m_buffer_t *buffer, - 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); + g1m_free(*h); *h = NULL; return (err); } +/* ************************************************************************** */ +/* Decoding language files for fx-CG/Prizm (G3L) */ +/* ************************************************************************** */ /** - * g1m_decode_std_lang_cg: - * Decode fx-CG language files. + * g1m_decode_std_cg_lang: + * Decode fx-CG language file. * - * @arg handle the libg1m handle. + * @arg h the handle to make. * @arg buffer the buffer to read from. * @arg std the standard header. + * @arg sub the standard subheader. + * @arg prizm the prizm-specific subheader. + * @arg check the checksum to feed. * @return the error code (0 if ok). */ -int g1m_decode_std_lang_cg(g1m_t *handle, g1m_buffer_t *buffer, - struct standard_header *std) +int g1m_decode_std_cg_lang(g1m_t **h, g1m_buffer_t *buffer, + struct standard_header *std, struct standard_subheader *sub, + struct _prizm_subheader *prizm, uint32_t *check) { - /* set handle type */ - handle->messages = NULL; + int err; /* read the subheader */ - DREAD(hd, g3l_subheader) + DREAD(lhd, g3l_lang_header) + *check = g1m_checksum32(&lhd, sizeof(struct g3l_lang_header), *check); - /* correct the endianness */ - hd.checksum = be32toh(hd.checksum); - hd.message_zone_size = be32toh(hd.message_zone_size); + /* read the data */ + size_t data_size = sub->filesize - sizeof(struct standard_header) + - sizeof(struct standard_subheader) - sizeof(struct _prizm_subheader) + - sizeof(struct g3l_lang_header) - 4; + uint8_t data[data_size]; + READ(data, data_size) + *check = g1m_checksum32(data, data_size, *check); - /* 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); + /* make the handle */ + int num = be32toh(lhd.num); + err = g1m_make_lang(h, g1m_platform_cg, num); + if (err) return (err); + g1m_t *handle = *h; - /* 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); + /* setup the pointers */ + uint32_t *offsets = (void*)data; + uint8_t *messages = (uint8_t*)&offsets[num + 1]; - /* 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_decode_lang_cg_content(handle, buffer, zonesize, &checksum); - else return (g1m_error_magic); - if (err) return (g1m_error_magic); + /* read messages */ + for (handle->count = 0; handle->count < (int)num; handle->count++) { + int i = handle->count; + if (offsets[i] == (uint32_t)-1) { + log_info("[#%d] -", i); + continue; + } - /* check the checksum */ - if (hd.checksum && hd.checksum != checksum) { - log_fatal("header checksum was invalid"); - goto fail; + /* correct offset and log */ + offsets[i] = be32toh(offsets[i]); + log_info("[#%d] '%s' (0x%" PRIu32 ")", i, + &messages[offsets[i]], offsets[i]); + + /* store */ + handle->messages[i] = strdup((char*)&messages[offsets[i]]); + if (!handle->messages[i]) 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 */ + /* done */ return (0); - /* omg fail! */ fail: - g1m_free_content(handle); - return (g1m_error_alloc); + g1m_free(*h); *h = NULL; + return (err); } diff --git a/src/decode/std/mcs.c b/src/decode/std/mcs.c index 894a877..66eb030 100644 --- a/src/decode/std/mcs.c +++ b/src/decode/std/mcs.c @@ -201,30 +201,27 @@ int g1m_decode_mcsfile_data(g1m_mcsfile_t **handle, * g1m_decode_std_mcs: * Decode an MCS file, after the Standard Header. * - * @arg handle the handle. + * @arg h the handle to make. * @arg buffer the buffer to read from. * @arg num number of sizes. * @return the error code (0 if ok). */ -int g1m_decode_std_mcs(g1m_t *handle, g1m_buffer_t *buffer, +int g1m_decode_std_mcs(g1m_t **h, g1m_buffer_t *buffer, struct standard_header *std) { int err = g1m_error_alloc; /* get number of subparts from the standard header */ uint_fast16_t num = std->number; - /* allocate memory for the files index */ - handle->count = 0; - handle->_size = num; - if (num) { - handle->files = malloc(sizeof(g1m_mcsfile_t*) * num); - if (!handle->files) return (g1m_error_alloc); - } + /* make the handle */ + err = g1m_make_mcs(h, num); + if (err) return (err); + g1m_t *handle = *h; /* read all of the parts */ log_info("%" PRIuFAST16 " total mcs files to browse", num); - while (handle->count < (int)num) { + for (handle->count = 0; handle->count < (int)num;) { /* get the subheader */ GDREAD(hd, mcs_subheader) @@ -267,6 +264,6 @@ int g1m_decode_std_mcs(g1m_t *handle, g1m_buffer_t *buffer, /* was error! */ fail: - g1m_free_mcs(handle); + g1m_free(*h); *h = NULL; return (err); } diff --git a/src/decode/std/picture.c b/src/decode/std/picture.c index 7e5a40d..4839102 100644 --- a/src/decode/std/picture.c +++ b/src/decode/std/picture.c @@ -45,7 +45,7 @@ static void g3p_deobfuscate(uint8_t *buf, size_t n) * @return the error code (0 if ok). */ -int g1m_decode_std_g3p(g1m_t *handle, g1m_buffer_t *buffer, +int g1m_decode_std_g3p(g1m_t **h, g1m_buffer_t *buffer, struct standard_header *std) { /* get the G3P global header */ @@ -60,8 +60,6 @@ int g1m_decode_std_g3p(g1m_t *handle, g1m_buffer_t *buffer, /* read the image header */ DREAD(ihd, g3p_imageheader) - - /* correct endianness */ ihd.df_size = be32toh(ihd.df_size); ihd.width = be16toh(ihd.width); ihd.height = be16toh(ihd.height); @@ -122,17 +120,14 @@ int g1m_decode_std_g3p(g1m_t *handle, g1m_buffer_t *buffer, } /* allocate picture in handle */ - unsigned int w = ihd.width, h = ihd.height; - handle->width = w; - handle->height = h; - handle->pixels = alloc_pixels(w, h); - if (!handle->pixels) return (g1m_error_alloc); - prepare_pixels(handle->pixels, w, h) + int err = g1m_make_picture(h, ihd.width, ihd.height); + if (err) return (err); + g1m_t *handle = *h; /* then store it */ g1m_decode_picture(handle->pixels, ihd.color_depth == g3p_color_4bit ? g1m_pictureformat_4bit_code : g1m_pictureformat_16bit, - inflated_image, w, h); + inflated_image, handle->width, handle->height); /* no error */ return (0); @@ -147,16 +142,16 @@ int g1m_decode_std_g3p(g1m_t *handle, g1m_buffer_t *buffer, * @return the error code (0 if ok). */ -int g1m_decode_std_c2p(g1m_t *handle, g1m_buffer_t *buffer, +int g1m_decode_std_c2p(g1m_t **h, g1m_buffer_t *buffer, struct standard_header *std) { (void)std; - (void)handle; + (void)h; (void)buffer; /* TODO */ log_info("C2P files are not managed yet."); log_info("Size is %" PRIuSIZE "B", sizeof(struct c2p_subheader)); /* no error */ - return (0); + return (g1m_error_magic); } diff --git a/src/manage/handle.c b/src/manage/handle.c index 8ac24ef..b681192 100644 --- a/src/manage/handle.c +++ b/src/manage/handle.c @@ -37,6 +37,7 @@ int g1m_make_picture(g1m_t **h, unsigned int width, unsigned int height) *h = malloc(sizeof(g1m_t)); if (!*h) return (g1m_error_alloc); g1m_t *handle = *h; + memset(handle, 0, sizeof(g1m_t)); /* allocate the pixels */ handle->width = width; @@ -63,67 +64,209 @@ int g1m_make_mcs(g1m_t **h, int count) /* allocate the handle */ *h = malloc(sizeof(g1m_t)); if (!*h) return (g1m_error_alloc); + g1m_t *handle = *h; + memset(handle, 0, sizeof(g1m_t)); /* initialize it */ - g1m_t *handle = *h; handle->type = g1m_type_mcs; handle->platform = g1m_platform_fx; + handle->count = 0; handle->_size = 0; + handle->files = NULL; /* allocate space */ - handle->count = count; handle->_size = count; - handle->files = NULL; if (count) { handle->files = malloc(sizeof(g1m_mcsfile_t*) * count); - if (!handle->files) { free(*h); *h = NULL; return (g1m_error_alloc); } + if (!handle->files) goto fail; memset(handle->files, 0, sizeof(g1m_mcsfile_t*) * count); + handle->_size = count; } /* no error */ return (0); +fail: + g1m_free(*h); *h = NULL; + return (g1m_error_alloc); +} + +/** + * g1m_make_fkey: + * Make a Function Keys handle. + * + * @arg h pointer to the handle to create. + * @arg pf the platform. + * @arg count the number of slots in the index. + * @return the error code (0 if ok). + */ + +int g1m_make_fkey(g1m_t **h, g1m_platform_t pf, int count) +{ + /* allocate the handle */ + *h = malloc(sizeof(g1m_t)); + if (!*h) return (g1m_error_alloc); + g1m_t *handle = *h; + memset(handle, 0, sizeof(g1m_t)); + + /* initialize it */ + handle->type = g1m_type_fkey; + handle->platform = pf; + handle->count = 0; handle->_size = 0; + handle->fkeys = NULL; + + /* allocate index */ + if (count) { + handle->fkeys = malloc(sizeof(uint32_t**) * count); + if (!handle->fkeys) goto fail; + memset(handle->fkeys, 0, sizeof(uint32_t**) * count); + handle->_size = count; + } + + /* no error */ + return (0); +fail: + g1m_free(*h); *h = NULL; + return (g1m_error_alloc); +} + +/** + * g1m_make_lang: + * Make a language handle. + * + * @arg h pointer to the handle to create. + * @arg platform the platform. + * @arg count the number of slots in the index. + * @return the error code (0 if ok). + */ + +int g1m_make_lang(g1m_t **h, g1m_platform_t platform, int count) +{ + /* allocate the handle */ + *h = malloc(sizeof(g1m_t)); + if (!*h) return (g1m_error_alloc); + g1m_t *handle = *h; + memset(handle, 0, sizeof(g1m_t)); + + /* initialize it */ + handle->type = g1m_type_lang; + handle->platform = platform; + handle->count = 0; handle->_size = 0; + handle->messages = NULL; + + /* allocate index */ + if (count) { + handle->messages = malloc(sizeof(char*) * count); + if (!handle->messages) goto fail; + memset(handle->messages, 0, sizeof(char*) * count); + handle->_size = count; + } + + /* no error */ + return (0); +fail: + g1m_free(*h); *h = NULL; + return (g1m_error_alloc); +} + +/** + * g1m_make_addin: + * Make an add-in handle. + * + * @arg h pointer to the handle to create. + * @arg platform the platform for which to make the add-in. + * @arg version the version of the add-in. + * @arg created the creation date of the add-in. + * @return the error code (0 if ok). + */ + +int g1m_make_addin(g1m_t **h, g1m_platform_t platform, + const g1m_version_t *version, const time_t *created) +{ + *h = NULL; + + /* check the platform */ + if (platform != g1m_platform_fx && platform != g1m_platform_cg + && platform != g1m_platform_cp) + return (g1m_error_op); + + /* allocate the handle */ + *h = malloc(sizeof(g1m_t)); + if (!*h) return (g1m_error_alloc); + g1m_t *handle = *h; + memset(handle, 0, sizeof(g1m_t)); + + /* set basic options */ + handle->platform = platform; + handle->version = *version; + handle->creation_date = *created; + + /* check the platform */ + unsigned int width, height; + switch (platform) { + case g1m_platform_fx: + width = G1A_ICON_WIDTH; + height = G1A_ICON_HEIGHT; + break; + case g1m_platform_cp: + width = C1A_ICON_WIDTH; + height = C1A_ICON_HEIGHT; + break; + default: /* case g1m_platform_cg: */ + width = G3A_ICON_WIDTH; + height = G3A_ICON_HEIGHT; + } + + /* allocate pictures */ + handle->icon_unsel = alloc_pixels(width, height); + if (!handle->icon_unsel) goto fail; + handle->icon_sel = alloc_pixels(width, height); + if (!handle->icon_sel) goto fail; + + /* prepare pictures */ + prepare_pixels(handle->icon_unsel, width, height) + prepare_pixels(handle->icon_sel, width, height) + + /* end my suffering */ + return (0); +fail: + g1m_free(*h); *h = NULL; + return (g1m_error_alloc); } /* ************************************************************************** */ /* Free a handle */ /* ************************************************************************** */ /** - * g1m_free_mcs: - * Free all of the MCS. + * g1m_free: + * Free a handle and its data. * - * @arg handle the handle to close. + * @arg handle the handle. */ -void g1m_free_mcs(g1m_t *handle) +void g1m_free(g1m_t *handle) { - /* check if mcs */ - if (!handle->files) - return ; + /* check if there is something to free */ + if (!handle) return ; - /* foreach file in mcs */ - g1m_mcsfile_t **files = handle->files; - int file_count = handle->count; - for (int i = 0; i < file_count; i++) { - /* free the file if exists */ - if (files[i]) g1m_free_mcsfile(files[i]); - } - free(handle->files); handle->files = NULL; -} - -/** - * g1m_free_content: - * Free handle data. - * - * @arg handle the handle to close. - */ - -void g1m_free_content(g1m_t *handle) -{ /* addin time! */ - if (handle->type & g1m_type_addin) - free(handle->pixels); + if (handle->type & g1m_type_addin) { + free(handle->icon_unsel); + free(handle->icon_sel); + } /* mcs time! */ - if (handle->type & g1m_type_mcs) - g1m_free_mcs(handle); + if (handle->type & g1m_type_mcs) { + /* check if mcs */ + if (!handle->files) + return ; + + /* foreach file in mcs */ + g1m_mcsfile_t **files = handle->files; + int file_count = handle->count; + for (int i = 0; i < file_count; i++) { + /* free the file if exists */ + if (files[i]) g1m_free_mcsfile(files[i]); + } + free(handle->files); handle->files = NULL; + } /* messages time! */ if (handle->type & g1m_type_lang @@ -148,18 +291,7 @@ void g1m_free_content(g1m_t *handle) /* e-activities time! */ if (handle->type & g1m_type_eact) g1m_free_line_content(handle->line); -} -/** - * g1m_free: - * Free a handle and its data. - * - * @arg handle the handle. - */ - -void g1m_free(g1m_t *handle) -{ - if (!handle) return ; - g1m_free_content(handle); + /* free the handle itself! */ free(handle); } diff --git a/src/type/std.c b/src/type/std.c index c153a75..3323601 100644 --- a/src/type/std.c +++ b/src/type/std.c @@ -18,14 +18,13 @@ * ************************************************************************** */ #include #include +#define f_c1 g1m_stdflag_check1 +#define f_c2 g1m_stdflag_check2 +#define f_sub g1m_stdflag_sub /* ************************************************************************** */ /* Local types */ /* ************************************************************************** */ -/* Flags */ -#define nochk_controlone 0x01 -#define nochk_controltwo 0x02 - /* Subtype correspondance type */ struct type_info { /* identification */ @@ -65,29 +64,29 @@ static struct main_info types[] = { /* USBPower (the most common one) */ {"USBPower", (struct type_info[]){ /* add-ins */ - {"\xf3" magic_common, "add-in", 0, + {"\xf3" magic_common, "add-in", f_c1 | f_c2, g1m_platform_fx, g1m_type_addin}, - {"\x2c" cp_magic, "fx-CG add-in", 0, + {"\x2c" cp_magic, "fx-CG add-in", f_c1 | f_c2 | f_sub, g1m_platform_cg, g1m_type_addin}, /* MCS */ - {"\x62" magic_common, "mcs (g2r)", 0, + {"\x62" magic_common, "mcs (g2r)", f_c1 | f_c2, g1m_platform_fx, g1m_type_mcs}, /* TODO: add flag? */ - {"\x31" magic_common, "mcs", 0, + {"\x31" magic_common, "mcs", f_c1 | f_c2, g1m_platform_fx, g1m_type_mcs}, - {"\x75" magic_common, "mcs (fx-CG)", 0, + {"\x75" magic_common, "mcs (fx-CG)", f_c1 | f_c2, g1m_platform_cg, g1m_type_mcs}, /* Language */ - {"\x12" magic_common, "fx language file", 0, + {"\x12" magic_common, "fx language file", f_c1 | f_c2, g1m_platform_fx, g1m_type_lang}, /* E-Activities */ - {"\x49" magic_common, "e-activity (document)", 0, + {"\x49" magic_common, "e-activity (document)", f_c1 | f_c2, g1m_platform_fx, g1m_type_eact}, /* Pictures */ - {"\x7d" magic_common, "fx-CG picture", 0, + {"\x7d" magic_common, "fx-CG picture", f_c1 | f_c2, g1m_platform_cg, g1m_type_pict}, TTERM @@ -95,7 +94,7 @@ static struct main_info types[] = { /* Ly755 (Classpad-specific) */ {"Ly755 ", (struct type_info[]){ - {"\x2c" cp_magic, "fx-CG language file", 0, + {"\x2c" cp_magic, "fx-CG language file", f_c1 | f_c2 | f_sub, g1m_platform_cg, g1m_type_lang}, TTERM @@ -103,7 +102,7 @@ static struct main_info types[] = { /* CASIO (only used for c2p...?) */ {"CASIO\0\0\0", (struct type_info[]){ - {"c2p\0\0\0", "Classpad picture", nochk_controlone | nochk_controltwo, + {"c2p\0\0\0", "Classpad picture", 0, g1m_platform_cp, g1m_type_pict}, TTERM @@ -125,20 +124,21 @@ struct ext_corresp { const char *info; unsigned int platform; unsigned int libtype; + unsigned int flags; }; /* Extension correspondances */ static struct ext_corresp ext_types[] = { /* fx types with non-checked header */ - {"g1l", "fx language file", g1m_platform_fx, g1m_type_lang}, - {"g1n", "fx fkeys file", g1m_platform_fx, g1m_type_fkey}, + {"g1l", "fx language file", g1m_platform_fx, g1m_type_lang, 0}, + {"g1n", "fx fkeys file", g1m_platform_fx, g1m_type_fkey, 0}, /* cg types with non-checked header */ - {"g3l", "fx-CG language file", g1m_platform_cg, g1m_type_lang}, - {"g3n", "fx-CG fkeys file", g1m_platform_cg, g1m_type_fkey}, + {"g3l", "fx-CG language file", g1m_platform_cg, g1m_type_lang, f_sub}, + {"g3n", "fx-CG fkeys file", g1m_platform_cg, g1m_type_fkey, f_sub}, /* sentinel */ - {NULL, NULL, 0, 0} + {NULL, NULL, 0, 0, 0} }; /* ************************************************************************** */ @@ -161,7 +161,7 @@ static struct ext_corresp ext_types[] = { int g1m_maketype_std(const char *path, unsigned char *main_id, unsigned char *subtype, - const char **info, int *check_one, int *check_two, + const char **info, unsigned int *flags, unsigned int *platform, g1m_type_t *type) { /* look if blank */ @@ -185,8 +185,7 @@ int g1m_maketype_std(const char *path, if (info) *info = e->info; if (platform) *platform = e->platform; if (type) *type = e->libtype; - if (check_one) *check_one = 0; - if (check_two) *check_two = 0; + if (flags) *flags = e->flags; } else { /* look for main_id */ struct main_info *mt = types - 1; @@ -217,8 +216,7 @@ int g1m_maketype_std(const char *path, if (info) *info = sub->info; if (platform) *platform = sub->platform; if (type) *type = sub->libtype; - if (check_one) *check_one = ~sub->flags & nochk_controlone; - if (check_two) *check_two = ~sub->flags & nochk_controltwo; + if (flags) *flags = sub->flags; } return (0); } diff --git a/src/type/sub.c b/src/type/sub.c new file mode 100644 index 0000000..6b551f0 --- /dev/null +++ b/src/type/sub.c @@ -0,0 +1,94 @@ +/* ***************************************************************************** + * type/sub.c -- extract the file type out of raw substd identification data. + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libg1m. + * libg1m is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libg1m is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libg1m; if not, see . + * ************************************************************************** */ +#include +#include + +/* ************************************************************************** */ +/* Local types */ +/* ************************************************************************** */ +/* Type correspondance type */ +struct type_info { + /* identification */ + int raw_platform; + int raw_type; + + /* types */ + g1m_platform_t platform; + g1m_type_t type; +}; + +/* ************************************************************************** */ +/* Correspondances */ +/* ************************************************************************** */ +/* Terminate type list */ +#define TTERM {0, 0, 0, 0} + +/* Platforms and types */ +#define p_cp 0x00 +#define p_cg 0x01 +#define t_addin 0x01 +#define t_fkey 0x02 +#define t_lang 0x04 + +/* Main types */ +static struct type_info types[] = { + /* fx-CP types */ + {p_cp, t_addin, g1m_platform_cp, g1m_type_addin}, + + /* fx-CG types */ + {p_cg, t_addin, g1m_platform_cg, g1m_type_addin}, + {p_cg, t_fkey, g1m_platform_cg, g1m_type_fkey}, + {p_cg, t_lang, g1m_platform_cg, g1m_type_lang}, + + /* sentinel */ + TTERM +}; + +/* ************************************************************************** */ +/* Main functions */ +/* ************************************************************************** */ +/** + * g1m_maketype_sub: + * Get type info from Standard Subheader. + * + * @arg raw_type The raw type. + * @arg raw_pf The raw platform. + * @arg flags The flags to set. + * @arg type The type to get. + * @arg platform The platform to get. + * @return If there was an error. + */ + +int g1m_maketype_sub(int raw_type, int raw_pf, unsigned int *flags, + g1m_type_t *type, g1m_platform_t *platform) +{ + /* look for the correspondance */ + struct type_info *c = types - 1; + while ((++c)->platform) { + if (c->raw_type == raw_type && c->raw_platform == raw_pf) + break; + } + if (!c->platform) return (g1m_error_unknown); + + /* fill things */ + if (flags) *flags = 0; + if (platform) *platform = c->platform; + if (type) *type = c->type; + return (0); +} diff --git a/src/utils/date.c b/src/utils/date.c new file mode 100644 index 0000000..459e775 --- /dev/null +++ b/src/utils/date.c @@ -0,0 +1,50 @@ +/* ***************************************************************************** + * utils/date.c -- decode and encode a date. + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libg1m. + * libg1m is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libg1m is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libg1m; if not, see . + * ************************************************************************** */ +#include + +/** + * g1m_decode_date: + * Decode a date from a string. + * + * @arg c the raw string. + * @arg t the destination timestamp. + * @return the error code (0 if ok). + */ + +int g1m_decode_date(const char *c, time_t *t) +{ + /* TODO: make checks on structure */ + + /* helper values */ + const int two = '0' + '0' * 10; + const int four = two + '0' * 100 + '0' * 1000; + + /* get the tm structure */ + struct tm date = { + .tm_year = c[0] * 1000 + c[1] * 100 + c[2] * 10 + c[3] - four - 1900, + .tm_mon = c[5] * 10 + c[6] - two - 1, + .tm_mday = c[7] * 10 + c[8] - two, + .tm_hour = c[10] * 10 + c[11] - two, + .tm_min = c[12] * 10 + c[13] - two + }; + + /* set the timestamp, return */ + *t = mktime(&date); + return (0); +} diff --git a/src/utils/skip.c b/src/utils/skip.c index 620c652..6e17f2b 100644 --- a/src/utils/skip.c +++ b/src/utils/skip.c @@ -27,10 +27,10 @@ * @arg checksum pointer to the checksum variable if there is one. */ -int g1m_skip(g1m_buffer_t *buffer, size_t size, uint_fast32_t *checksum) +int g1m_skip(g1m_buffer_t *buffer, size_t size, uint32_t *checksum) { uint8_t buf[1024]; int err; - uint_fast32_t add = 0; + uint32_t add = 0; size_t orig = size; while (size) { diff --git a/src/utils/version.c b/src/utils/version.c new file mode 100644 index 0000000..883738d --- /dev/null +++ b/src/utils/version.c @@ -0,0 +1,47 @@ +/* ***************************************************************************** + * utils/version.c -- decode and encode a version. + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libg1m. + * libg1m is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libg1m is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libg1m; if not, see . + * ************************************************************************** */ +#include + +/** + * g1m_decode_version: + * Decode a version from a 'MM.mm.ffff' formatted version string. + * + * @arg raw the string to decode from. + * @arg version the structure where to store the information. + * @return the error code (if any). + */ + +int g1m_decode_version(const char *raw, g1m_version_t *version) +{ + /* TODO: make checks on format */ + + /* helper values */ + const int two = '0' + '0' * 10; + const int four = two + '0' * 100 + '0' * 1000; + + /* get the version data */ + *version = (g1m_version_t){ + .major = raw[0] * 10 + raw[1] - two, + .minor = raw[3] * 10 + raw[4] - two, + .revision = raw[6] * 1000 + raw[7] * 100 + raw[8] * 10 + raw[9] - four + }; + + /* no error */ + return (0); +} diff --git a/tools/write-header-config b/tools/write-header-config index fb1d712..6480166 100755 --- a/tools/write-header-config +++ b/tools/write-header-config @@ -17,15 +17,15 @@ esac; done # Make version as numbers vnum=$(echo ${version} | cut -d- -f1) -version_major=$(( $(echo ${vnum} | cut -d. -f1) )) -version_minor=$(( $(echo ${vnum} | cut -s -d. -f2) )) -version_rev=$(( $(echo ${vnum} | cut -s -d. -f3) )) -version_indev=$(echo ${version} | cut -s -d- -f2) -version_indev=$([ ${version_indev} ] && echo 1 || echo 0) +version_major=$(printf "%d" "$(echo ${vnum} | cut -d. -f1)") +version_minor=$(printf "%d" "$(echo ${vnum} | cut -s -d. -f2)") +version_rev=$(printf "%d" "$(echo ${vnum} | cut -s -d. -f3)") +version_indev="$(echo ${version} | cut -s -d- -f2)" +version_indev="$([ "${version_indev}" ] && echo 1 || echo 0)" # Constitute version thingies version_num=$(printf "0x%02X%02X%04X" \ - ${version_major} ${version_minor} ${version_rev}) + "${version_major}" "${version_minor}" "${version_rev}") #******************************************************************************# # Write the file # @@ -58,11 +58,11 @@ cat <<_EOF #ifndef LIBG1M_CONFIG_H # define LIBG1M_CONFIG_H # define LIBG1M_VERSION "${version}" -# define LIBG1M_VERNUM ${version_num} -# define LIBG1M_MAJOR ${version_major} -# define LIBG1M_MINOR ${version_minor} -# define LIBG1M_REV ${version_rev} -# define LIBG1M_INDEV ${version_indev} +# define LIBG1M_VERNUM ${version_num} +# define LIBG1M_MAJOR ${version_major} +# define LIBG1M_MINOR ${version_minor} +# define LIBG1M_REV ${version_rev} +# define LIBG1M_INDEV ${version_indev} # define LIBG1M_MAINTAINER \\ "${maintainer}"