Corrected CasEmul management (endianness)
This commit is contained in:
parent
23f40d1695
commit
88fcbeeb85
|
@ -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 \
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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_) */
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
* have while in the MCS, but the format CasEmul uses internally.
|
||||
* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
#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++;
|
||||
}
|
||||
|
|
Reference in New Issue