From 88fcbeeb85daafb48841011f75328588b7552dbf Mon Sep 17 00:00:00 2001 From: "Thomas \"Cakeisalie5\" Touhey" Date: Sun, 5 Mar 2017 13:56:57 +0100 Subject: [PATCH] Corrected CasEmul management (endianness) --- Makefile.vars | 2 +- include/libg1m/format/casemul.h | 43 +++++++++++++++----------- include/libg1m/handle.h | 35 ++++++++++++--------- src/core/decode.c | 8 ++--- src/decode/casemul.c | 55 ++++++++++++++++++++------------- 5 files changed, 84 insertions(+), 59 deletions(-) diff --git a/Makefile.vars b/Makefile.vars index 97dc4fb..267df4f 100755 --- a/Makefile.vars +++ b/Makefile.vars @@ -70,7 +70,7 @@ # Compiler CC := $(TARGET)gcc # - Check flags (warnings) - CWERROR := all extra no-attributes no-unused-macros no-vla + CWERROR := all extra no-attributes no-unused-macros no-vla no-multichar ifdef MORE_WARNINGS CWERROR += shadow write-strings redundant-decls format format-nonliteral \ format-security implicit-function-declaration \ diff --git a/include/libg1m/format/casemul.h b/include/libg1m/format/casemul.h index 5972823..b057483 100644 --- a/include/libg1m/format/casemul.h +++ b/include/libg1m/format/casemul.h @@ -22,13 +22,12 @@ # pragma pack(1) /* Casemul files are made of an overall header, a source part and an - * optional compiled part. One thing to know is that every header has an - * internal header, which always has this structure: */ + * optional compiled part. One thing to know is that every specific header has + * an internal header, which always has this structure: */ struct casemul_internal_header { - /* this type was made using `MAKELONG`: you have to reverse the bytes in - * each word (e.g. AC-FS -> CA-SF). */ - char type[4]; + /* this signature, a set of 4 characters identifying the header type */ + uint32_t signature; /* version (more or less 0xMMmm, where MM is the major and mm the minor) */ uint32_t version; @@ -37,27 +36,35 @@ struct casemul_internal_header { uint32_t size; }; +/* The signature is made using `MAKELONG`, a Microsoft Windows specific macro + * which concatenates two words (16-bit integers) into a single long + * (32-bit integer). One usage in CasEmul is the following: + * + * MAKELONG('CA', 'FS') -- for the overall header's internal header. + * + * And here is the macro: */ + +#ifndef MAKELONG /* XXX: does it work with big endian platforms? */ +# define MAKELONG(a, b) \ + ((uint32_t) (((uint16_t) (((uint32_t) (a)) & 0xffff)) | \ + ((uint32_t) ((uint16_t) (((uint32_t) (b)) & 0xffff))) << 16)) +#endif + /* The multi-byte values do not have a defined endianness! The Casemul files * are generated from C++ classes, so when one is generated, the host's - * endianness is used. Also, there is no setting anywhere in the file to say - * whether the host was big endian or little endian. - * - * Actually, there is a way to guess using the magic, which is made using - * `MAKELONG`, which concatenates two words ('CA' and 'FS', 'AC' and 'SF' in - * little endian). That means if it is "CASF", then the file was generated on - * a big endian platform, and if it is "ACFS", it was generated on a little - * endian platform. - * - * The libg1m implementation just supposes the file was generated on a - * little-endian platform, as CasEmul was used when Microsoft Windows was - * only implemented on x86-like platforms (little endian). */ + * endianness is used. There is no setting anywhere related to endianness, + * but the endianness can be guessed using the magic word: if it is + * "CASF", then the platform was big endian, otherwise ("ACFS"), the platform + * was little endian (very probable, as Microsoft Windows is mostly used on + * the x86 architecture, which is little endian). */ /* ************************************************************************** */ /* Overall, source and compiled headers */ /* ************************************************************************** */ /* The overall header contains information about the other sections, and the * current state of the file (whether it contains the compiled part or not). * - * Its internal header ID is "CAFS" (ACFS, used as the magic string). + * Its internal header ID is MAKELONG('CA', 'FS') (abbreviated "CAFS" for + * simplicity, see the 'problem' with endianness above). * The expected version is 1.00. */ # define casemul_compiled 0x80 /* if the compiled program is there */ diff --git a/include/libg1m/handle.h b/include/libg1m/handle.h index e4857b2..c856d8d 100644 --- a/include/libg1m/handle.h +++ b/include/libg1m/handle.h @@ -27,13 +27,13 @@ /* ************************************************************************** */ /* General type */ typedef unsigned int g1m_type_t; -# define g1m_type_addin 0x01 -# define g1m_type_mcs 0x02 -# define g1m_type_eact 0x04 -# define g1m_type_picture 0x08 -# define g1m_type_lang 0x10 -# define g1m_type_fkey 0x20 -# define g1m_type_storage 0x40 +# define g1m_type_addin 0x0001 +# define g1m_type_mcs 0x0002 +# define g1m_type_eact 0x0004 +# define g1m_type_picture 0x0008 +# define g1m_type_lang 0x0010 +# define g1m_type_fkey 0x0020 +# define g1m_type_storage 0x0040 /* General type aliases */ # define g1m_type_pict g1m_type_picture @@ -42,10 +42,18 @@ typedef unsigned int g1m_type_t; /* Platform */ typedef unsigned int g1m_platform_t; -# define g1m_platform_none 0x00 -# define g1m_platform_fx 0x01 -# define g1m_platform_cp 0x02 -# define g1m_platform_cg 0x04 +# define g1m_platform_none 0x0000 +# define g1m_platform_fx 0x0001 +# define g1m_platform_cp 0x0002 +# define g1m_platform_cg 0x0004 +# define g1m_platform_cas 0x0008 +# define g1m_platform_casemul 0x0010 + +/* Platform flags */ +# define g1m_platflag_be 0x8000 + +/* Platform macros */ +# define g1m_platform(P) ((P) & 0x7FFF) /* ************************************************************************** */ /* Helpers */ @@ -61,10 +69,9 @@ typedef struct g1m_version_s { /* Handle structure */ /* ************************************************************************** */ typedef struct { - /* g1m type */ + /* file type, the source destination platform */ g1m_type_t type; - /* for what platform is this type made for? */ - g1m_platform_t platform; + g1m_platform_t platform; /* use the `g1m_platorm` macro! */ /* Add-in related data */ char title[17]; diff --git a/src/core/decode.c b/src/core/decode.c index d65642f..f67f182 100644 --- a/src/core/decode.c +++ b/src/core/decode.c @@ -36,7 +36,7 @@ int g1m_decode(g1m_t *handle, const char *path, g1m_buffer_t *buffer, g1m_type_t expected_types) { /* initialize the handle */ - bzero(handle, sizeof(g1m_t)); + memset(handle, 0, sizeof(g1m_t)); /* identify a CAS file */ unsigned char buf[0x20]; READ(buf, 1) @@ -45,9 +45,9 @@ int g1m_decode(g1m_t *handle, const char *path, g1m_buffer_t *buffer, /* identify a Casemul file */ READ(&buf[1], 3) - log_info("Buffer content is:"); - logm_info(buf, 4); - if (!memcmp(buf, "ACFS", 4)) + if (!memcmp(buf, "CAFS", 4)) { + handle->platform |= g1m_platflag_be; + } else if (!memcmp(buf, "ACFS", 4)) return (g1m_decode_casemul(handle, buffer)); /* identify a standard header (send a _copy_) */ diff --git a/src/decode/casemul.c b/src/decode/casemul.c index 016d237..9bc2273 100644 --- a/src/decode/casemul.c +++ b/src/decode/casemul.c @@ -24,7 +24,9 @@ * have while in the MCS, but the format CasEmul uses internally. * ************************************************************************** */ #include -#define VER 0x00000100 /* 1.00, the expected version in the internal headers */ +#define e32toh(N) ((big_endian) ? be32toh(N) : le32toh(N)) +#define htoe32(N) ((big_endian) ? htobe32(N) : htole32(N)) +#define VER htoe32(0x00000100) /* 1.00 */ /* ************************************************************************** */ /* Utilities */ @@ -39,18 +41,17 @@ * @return the error (if any). */ -static int read_internal(g1m_buffer_t *buffer, const char *type, +static int read_internal(g1m_buffer_t *buffer, uint32_t signature, uint_fast32_t version) { DREAD(hd, casemul_internal_header) /* check type */ - if (type[0] != hd.type[1] || type[1] != hd.type[0] - || type[2] != hd.type[3] || type[3] != hd.type[2]) { - log_error("type mismatch!"); + if (signature != hd.signature) { + const char *a = (char*)&signature, *b = (char*)&hd.signature; + log_error("signature mismatch!"); log_error("expected '%c%c%c%c', got '%c%c%c%c'", - type[0], type[1], type[2], type[3], - hd.type[1], hd.type[0], hd.type[3], hd.type[2]); + a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3]); return (g1m_error_magic); } @@ -59,7 +60,7 @@ static int read_internal(g1m_buffer_t *buffer, const char *type, log_error("version mismatch!"); log_error("version mismatch!"); log_error("epxected %08" PRIxFAST32 ", got %08" PRIx32, - version, le32toh(hd.version)); + version, hd.version); return (g1m_error_magic); } @@ -108,10 +109,12 @@ static int read_top(g1m_buffer_t *buffer, char *name, uint_fast32_t *length) * * @arg pfile the pointer to the mcsfile to make and fill. * @arg buffer the buffer to read from. + * @arg big_endian whether the file is big endian or not. * @return the error (if occured). */ -static int read_picture(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer) +static int read_picture(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer, + int big_endian) { int err; *pfile = NULL; static const uint32_t colours[256] = { @@ -125,7 +128,7 @@ static int read_picture(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer) /* general record things */ char name[13]; uint_fast32_t record_length; if ((err = read_top(buffer, name, &record_length)) - || (err = read_internal(buffer, "PICT", VER))) + || (err = read_internal(buffer, MAKELONG('PI', 'CT'), VER))) return (err); /* specific things */ @@ -170,17 +173,19 @@ static int read_picture(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer) * * @arg pfile the pointer to the mcsfile to make and fill. * @arg buffer the buffer to read from. + * @arg big_endian whether the file is big endian encoded or not. * @return the error that occured. */ -static int read_matrix(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer) +static int read_matrix(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer, + int big_endian) { int err; /* general record things */ char name[13]; uint_fast32_t record_length; if ((err = read_top(buffer, name, &record_length)) - || (err = read_internal(buffer, "MTRX", VER))) + || (err = read_internal(buffer, MAKELONG('MT', 'RX'), VER))) return (err); /* read specific things */ @@ -235,17 +240,19 @@ static int read_matrix(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer) * * @arg pfile the pointer to the mcsfile to make and fill. * @arg buffer the buffer to read from. + * @arg big_endian whether the file is big endian encoded or not. * @return the error that occured. */ -static int read_list(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer) +static int read_list(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer, + int big_endian) { int err; /* general record things */ char name[13]; uint_fast32_t record_length; if ((err = read_top(buffer, name, &record_length)) - || (err = read_internal(buffer, "LIST", VER))) + || (err = read_internal(buffer, MAKELONG('LI', 'ST'), VER))) return (err); /* read specific things */ @@ -303,19 +310,20 @@ static int read_list(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer) int g1m_decode_casemul(g1m_t *handle, g1m_buffer_t *buffer) { - int err; + int err, big_endian = handle->platform & g1m_platflag_be; /* read the overall header's internal header end */ - struct casemul_internal_header overall_int = {.type = "ACFS"}; + struct casemul_internal_header overall_int = { + .signature = MAKELONG('CA', 'FS')}; READ(&overall_int.version, 8); - if (le32toh(overall_int.version) != 0x100) + if (e32toh(overall_int.version) != 0x100) return (g1m_error_magic); /* read the overall (global) header */ DREAD(glb, casemul_header) /* read the source header */ - if ((err = read_internal(buffer, "SRCE", VER))) + if ((err = read_internal(buffer, MAKELONG('SR', 'CR'), VER))) return (err); DREAD(src, casemul_source_header) log_info("%d program(s), %d pic(s), %d matrix(es), %d list(s)", @@ -336,7 +344,7 @@ int g1m_decode_casemul(g1m_t *handle, g1m_buffer_t *buffer) /* general record things */ char name[13]; uint_fast32_t record_length; if ((err = read_top(buffer, name, &record_length)) - || (err = read_internal(buffer, "PROG", VER))) + || (err = read_internal(buffer, MAKELONG('PR', 'OG'), VER))) goto fail; /* specific things */ @@ -349,21 +357,24 @@ int g1m_decode_casemul(g1m_t *handle, g1m_buffer_t *buffer) /* read each picture */ for (int i = 0; i < src.pictures; i++) { - if ((err = read_picture(&handle->files[handle->count], buffer))) + if ((err = read_picture(&handle->files[handle->count], buffer, + big_endian))) goto fail; handle->count++; } /* read each matrix */ for (int i = 0; i < src.matrixes; i++) { - if ((err = read_matrix(&handle->files[handle->count], buffer))) + if ((err = read_matrix(&handle->files[handle->count], buffer, + big_endian))) goto fail; handle->count++; } /* read each list */ for (int i = 0; i < src.lists; i++) { - if ((err = read_list(&handle->files[handle->count], buffer))) + if ((err = read_list(&handle->files[handle->count], buffer, + big_endian))) goto fail; handle->count++; }