/* ***************************************************************************** * libg1m/format/std.h -- the CASIO "standard" file format description. * 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 . * ************************************************************************** */ #ifndef LIBG1M_FORMAT_STD_H # define LIBG1M_FORMAT_STD_H # include # pragma pack(1) /* This format is used by CASIO with its calculator for storing things * like add-ins (compiled programs), MCS (main memory saves), and others. * In fact, there is no name for the general format, only names for the * "subformats" based on this one. * * It all starts with a header, called Standard Header by Simon Lothar. * This Standard Header contains the total filesize, the G1M type (add-in, * MCS, e-acts), some data that will be useful for the MCS type, and some * magic and control bytes. * * For some reason, this StandardHeader is INVERTED on every file it's on, * you will have to apply a NOT operation on its bytes for it to make sense. * * Keep in mind that, everywhere in the header, multi-bytes integers * are BIG ENDIAN. * * The LSB is the Least Significant Byte: once adapted to the host endianness, * it can simply be obtained by bitwise-AND-ing with 0xff. */ struct standard_header { /* the file identifier */ uint8_t main_id[8]; /* our subtype! */ uint8_t subtype[6]; /* first control byte: filesize LSB + 0x41 */ uint8_t control; /* said to be 0x01, but doesn't need to */ uint8_t align_one; /* total filesize */ uint32_t filesize; /* second control byte: filesize LSB + 0xb8 */ uint8_t control2; /* alignment */ uint8_t align[8]; /* is obfuscated - useful for G3P */ uint8_t obfuscated; /* number of objects contained (useful for MCS filetype) */ uint16_t number; }; /* At the beginning, we thought "USBPower" was some magic sequence we would * systematically find in the "main_id" field. But counter examples came: * the G3L, whose main ID was "Ly755 ", and the C2P. * * 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`. * * After the Standard Header is read and the type is read, we have parts, * each with their own subheaders and their own subtilities. * * 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: * - 0x00: picture; * - 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[4]; /* size of the data + footer? */ uint32_t df_size; /* 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 the platform is the Prizm, 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 # include # include # include # include # include # include #endif /* LIBG1M_FORMAT_STD_H */