/* ***************************************************************************** * libg1m/format/storage.h -- the G1M storage 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_STORAGE_H # define LIBG1M_FORMAT_STORAGE_H /* Storage backup files (G1S) contain backups of the storage memory. * It corresponds exactly to the on-calc storage memory structure. * * According to Simon Lothar, the files contain zeroes up to 0x00270000 * (including the StandardHeader; this is the location of the SMEM in real * fx-9860 calculators), then it has the same structure than the one * used by the calculator. * * It looks like these files are not managed by the calculator (not directly); * however, they are managed by FA-124. */ /* ************************************************************************** */ /* Directory list */ /* ************************************************************************** */ /* The SMEM (content of the G1S file once 0x270000 bytes were skipped) starts * with a big entry list. An entry is 32-bytes long: it starts with a common * part, then with a type-specific part (then unused bytes if the subheader * is less than 28-bytes long). Here is its structure: */ struct storage_entry { /* the type number - see below */ uint16_t type; /* the number of the entry * if this field is 0x00, just ignore the entry. * we don't know why entries with this field set to 0x00 exist... * (probably backups?) */ uint16_t id; /* the raw subheader */ uint8_t raw_subheader[28]; }; /* The `type` is composed of three nibbles of identification (`type & 0xFFF`) * and a (high) nibble of special properties, keep that in mind. * Here are the known types: */ enum storage_entry_type { storage_entrytype_sector = 0x200, storage_entrytype_directory = 0x110, storage_entrytype_file = 0x120, storage_entrytype_fragment = 0x130, }; /* The special nibble is usually zero when deleted, and non-zero when active. * According to Simon Lothar, this is because it is possible to clear bits * on the flash by flashwriting a short value only; but when it comes to * set bits, it becomes more tricky. * * This is also why optimizing is not done straight away (to save the flash * a little). * * The list can have to 0x800 entries, and occupies the first sector. * Simon Lothar says the list can expand to the second sector (which is used * only if there is no space in the index sector or the other data sectors). * Entries with the 0xFFFF type should not be read. * * Some entry types can have parents. In this case, they have parent type, * and parent number fields. This is because elements with different types * follow different numerations; so the type field is here on which numeration * to search for the member. If the parent type is 0xFFFF (or -1, as some say), * then there is no parent. * * For now, only directories as parents for files have been encountered. */ /* ************************************************************************** */ /* Sectors */ /* ************************************************************************** */ /* Sectors represent the physical sectors of the storage memory (space!). * They are 64 KiB large. * * Simon Lothar says that there are 27 entries for them (or 14 on the AU models * because of australian exam regulations). If the logical number * is 0xFFFFFFFF, the sector is unused. (sometimes the logical number field * contains gibberish, I don't know why yet) * * Their special nibble is either 0x04 or 0x00, the signification of it is not * known to me yet. Here is their subheader structure: */ struct storage_sector { /* the sector start address - 0xFF */ uint32_t startaddr; /* the sector ID - probably not using the UUID so sectors can * not be set as parent nodes :) */ uint32_t logical_sector_number; }; /* ************************************************************************** */ /* Directories */ /* ************************************************************************** */ /* After the sectors come the directories. * * Their special nibble is either 0x05 if active or 0x00 if deleted. * Here is their subheader structure: */ struct storage_directory { /* the parent type and id */ uint16_t parent_type; uint16_t parent_id; /* the name (FONTCHARACTER-encoded) */ FONTCHARACTER name[12]; }; /* ************************************************************************** */ /* Files */ /* ************************************************************************** */ /* After the directories come the files (interrupted by fragments - see after). * * Their special nibble have the same meaning that for directories. * Here is their subheader structure: */ struct storage_file { /* the parent type and id (not functional?) */ uint16_t parent_type; uint16_t parent_id; /* the name (FONTCHARACTER-encoded) */ FONTCHARACTER name[12]; }; /* ************************************************************************** */ /* File fragments */ /* ************************************************************************** */ /* After each file entry comes the corresponding fragments entries. * Fragments are in fact links to the sectors, with some more info. * * Their special nibble have the same meaning that for files... but are not * always accurate, so only check on files! * * The first fragment corresponding to a file contain this file's type, * probably detected when the file is transferred. Here are the * known values: */ enum storage_file_type { g1m_storage_filetype_unknown = 0x01, g1m_storage_filetype_addin = 0x02, /* G1A */ g1m_storage_filetype_mcs = 0x06, /* G1M */ g1m_storage_filetype_g2r = 0x2E, /* G1R */ }; /* Why is the file type here and not in the file header? I don't know. * And here is the fragments' subheader structure: */ struct storage_fragment { /* the parent (file node) type and id */ uint16_t parent_type; uint16_t parent_id; /* the file type */ uint16_t file_type; /* the fragment count and id */ uint16_t fragment_count; uint16_t fragment_id; /* the sector id that the fragment is linked to */ uint16_t fragment_sector_id; /* number and offset in the sector of the fragment's bytes */ uint16_t data_offset; uint16_t data_length; }; #endif /* LIBG1M_FORMAT_STORAGE_H */