bfile: clean up header, add BFile_Seek and BFile_GetPos

This commit is contained in:
Lephe 2021-12-09 16:09:08 +01:00
parent 2e5e56f82e
commit 71de4dcb95
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
2 changed files with 245 additions and 86 deletions

View File

@ -1,9 +1,60 @@
//---
// gint:bfile - BFile interface
// gint:bfile - BFile interface
//
// The system's file system is a nightmare and the risk of corrupting it
// or the flash by driving it manually is too great to risk. This module
// interfaces with the BFile syscalls for file management.
// BFile is the OS's native interface to the filesystem. It has quite a bit of
// legacy, so it's not very easy to use. Please note that BFile cannot be used
// from within gint, so do a gint_world_switch() (<gint/gint.h>) to call BFile
// functions; otherwise the add-in is likely to crash.
//
// There are two different filesystems:
// * An older, in-house one made by CASIO and informally called CASIOWIN (which
// is the name of the OS itself), which is incomplete, hard to use, and very
// limiting, but also very fast.
// * A newer one, designed by Kyoto Software Research and called Fugue, which
// is mostly complete and behaves as a regular filesystem, though very slow.
//
// You can detect which one is used by querying gint[HWFS] (<gint/hardware.h>):
// * HWFS_FUGUE is the newer one; it is found in:
// - The fx-CG series (in France: Prizm and Graph 90+E)
// - The fx G-III series (in France: Graph 35+E II)
// * HWFS_CASIOWIN is the older one; it is found in all older black-and-white
// model that have a storage memory.
//
// Wherever Fugue is used, gint supports the Unix file API (open, read, write,
// etc) and the C99 standard file API (fopen, fread, fwrite, etc), so there is
// no need to call into BFile directly (you should still do a world switch
// before using these functions).
//
// You should only have to use BFile if you're dealing with the CASIOWIN
// filesystem. With Fugue, not only are the Unix/C99 APIs easier to use, but
// the meanings of arguments and return values in BFile calls also change
// compared to the CASIOWIN filesystem, so there's little to gain anyway.
//
// If you're here for the CASIOWIN filesystem, be aware of its limitations. In
// general reading files will work fine; expect no issues with that. Modifying
// files is where things get difficult. Keep the following in mind:
//
// * Non-standard meanings of arguments and return values (eg. BFile_Read()
// returns the number of readable bytes between the new position and EOF).
// * Files must be created with a fixed size indicated in BFile_Create() and
// are initialized with 0xff.
// * A write to a file can only replace 1's with 0's, meaning in practice a
// file can only be written to once. (That's why it's created with 0xff.)
// * All writes must be of even size; writing an odd number of bytes can mess
// up the file and confuse the filesystem.
// * Only one level of folders is supported; attempting to create second-level
// subfolders results in weird recursive directories (don't do that).
//
// File paths in the OS use the non-standard FONTCHARACTER encoding, which is a
// 16-bit fixed-width encoding. Most users don't care about special characters;
// in GCC, you can get a 16-bit literal string with the u"" prefix, eg.
// u"\\\\fls0\\data.bin"
// which is what you usually supply as the [uint16_t const *path] argument.
//
// All functions return nonnegative values on success. If no return value is
// described then a successful call returns 0. Unless specified otherwise, all
// functions can also return a negative error code as documented near the end
// of this header.
//---
#ifndef GINT_BFILE
@ -15,88 +66,162 @@ extern "C" {
#include <stdint.h>
/* BFile_Remove(): Remove a file
Also works if the file does not exist.
//---
// Common file access functions
//---
@file FONTCHARACTER file path
Returns a BFile error code. */
int BFile_Remove(uint16_t const *file);
/* Remove a file or folder (also works if the entry does not exist). */
int BFile_Remove(uint16_t const *path);
#define BFile_File 1
#define BFile_Folder 5
/* BFile_Create(): Create a new file or folder
/* BFile_Create(): Create a new entry
The file or directory must not exist. For a file the size pointer must point
to the desired file size (which is fixed), for a folder it is ignored.
to the desired file size (which is fixed), for a folder it is ignored. With
CASIOWIN this is the final size of the file. With Fugue the file can be
resized dynamically and is usually created with initial size 0.
@file FONTCHARACTER file path
@type Entry type
@size Pointer to file size if [type = BFile_File], use NULL otherwise
Returns a BFile error code. */
enum BFile_EntryType
{
BFile_File = 1,
BFile_Folder = 5,
};
int BFile_Create(uint16_t const *file, enum BFile_EntryType type, int *size);
@path FONTCHARACTER file path
@type Entry type (BFile_File or BFile_Folder)
@size Pointer to file size if type is BFile_File, use NULL otherwise */
int BFile_Create(uint16_t const *path, int type, int *size);
/* BFile_Open(): Open an existing file
The file must exist.
#define BFile_ReadOnly 0x01
#define BFile_WriteOnly 0x02
#define BFile_ReadWrite (BFile_ReadOnly | BFile_WriteOnly)
#define BFile_Share 0x80
#define BFile_ReadShare (BFile_ReadOnly | BFile_Share)
#define BFile_ReadWriteShare (BFile_ReadWrite | BFile_Share)
@file FONTCHARACTER file path
/* BFile_Open(): Open a file for reading or writing
The file must exist, even when opening in writing mode. The meaning of the
[Share] flag isn't clear; I believe it's simply ignored in the CASIOWIN
filesystem.
@path FONTCHARACTER file path
@mode Desired access mode
Returns a file descriptor on success, or a negative BFile error code. */
enum BFile_OpenMode
{
BFile_ReadOnly = 0x01,
BFile_WriteOnly = 0x02,
BFile_ReadWrite = BFile_ReadOnly | BFile_WriteOnly,
};
int BFile_Open(uint16_t const *file, enum BFile_OpenMode mode);
Returns a file descriptor (or a negative error code). */
int BFile_Open(uint16_t const *path, int mode);
/* BFile_Close(): Close a file descriptor
@fd Open file descriptor
Returns a BFile error code. */
/* Close an open file descriptor. */
int BFile_Close(int fd);
/* BFile_Size(): Retrieve size of an open file
@fd Open file descriptor
Returns the file size in bytes, or a negative BFile error code*/
/* Get the size of an open file. */
int BFile_Size(int fd);
/* BFile_Write(): Write data to an open file
Second and third argument specify the data and length to write.
WARNING: The file systems has shown to become inconsistent if an odd number
of bytes is written with BFile_Write(). Keep it even!
WARNING: The CASIOWIN fs is known to become inconsistent if an odd number of
bytes is written with BFile_Write(). Always keep it even!
@fd File descriptor open for writing
@data Data to write
@even_size Size to write (must be even, yes)
Returns a BFile error code. */
With CASIOWIN, returns the new file offset after writing (or an error code).
With Fugue, returns the amount of data written (or an error code). */
int BFile_Write(int fd, void const *data, int even_size);
/* BFile_Read(): Read data from an open file
The second and third argument specify where to store and how much to read.
The location from where the data is read depends on [whence]:
The extra argument [whence] specifies where data is read from in the style
of pread(2), and is supported with both filesystems.
* If [whence >= 0], it is taken as an absolute location within the file;
* If [whence == -1], BFile_Read() reads from the current position.
@fd File descriptor open for reading
@data Buffer of at least [size] bytes to store data to
@size Number of bytes to read
@whence Starting position of the data to read in the file
Returns a BFile error code. */
With CASIOWIN, returns how much data can be read from the updated file
offset (ie. how many bytes until end of file), or an error code.
With Fugue, returns the amount of data read (or an error code). */
int BFile_Read(int handle, void *data, int size, int whence);
/* BFile_FindFirst(): Search a directory for file with matching name
Doesn't work on Main memory. Only four search handle can be opened, you need
to close them to be able to reuse them. Search is NOT case sensitive and *
can be used as a wildcard.
/* BFile_Seek(): Seek to a relative or absolute position within an open file
With CASIOWIN, moves [offset] bytes relative to the current position, and
returns how much data can be read from the new position. BFile_Seek(fd, 0)
combined with BFile_Size(fd) can be used to determine the current position.
With Fugue, moves to the absolute position [offset] within the file, and
returns the amount of allocated space following the new position (usually
larger than the amount of data until end-of-file because files are allocated
in units of 4096 bytes). There is no way to seek relative to the current
position unless the target is precomputed with BFile_GetPos(). */
int BFile_Seek(int fd, int offset);
/* BFile_GetPos(): Get the current position in an open file
This call does not exist in the CASIOWIN interface, so this function always
returns -1 on models with a CASIOWIN filesystem.
This call exists in Fugue, however some Fugue models have their syscall list
inherited from the CASIOWIN era and don't have an entry point for it (or if
there's one it's escape scrutiny so far).
* Prizm / Graph 90+E / fx-CG series: this function works as intended.
* Graph 35+E II / G-III series: the call is missing, returns -1.
For the latter models there is no easily reliable way of knowing the current
position within an open file! */
int BFile_GetPos(int fd);
//---
// Error codes
//---
#define BFile_EntryNotFound -1
#define BFile_IllegalParam -2
#define BFile_IllegalPath -3
#define BFile_DeviceFull -4
#define BFile_IllegalDevice -5
#define BFile_IllegalFilesystem -6
#define BFile_IllegalSystem -7
#define BFile_AccessDenied -8
#define BFile_AlreadyLocked -9
#define BFile_IllegalTaskID -10
#define BFile_PermissionError -11
#define BFile_EntryFull -12
#define BFile_AlreadyExists -13
#define BFile_ReadOnlyFile -14
#define BFile_IllegalFilter -15
#define BFile_EnumerateEnd -16
#define BFile_DeviceChanged -17
//#define BFile_NotRecordFile -18 // Not used
#define BFile_IllegalSeekPos -19
#define BFile_IllegalBlockFile -20
//#define BFile_NoSuchDevice -21 // Not used
//#define BFile_EndOfFile -22 // Not used
#define BFile_NotMountDevice -23
#define BFile_NotUnmountDevice -24
#define BFile_CannotLockSystem -25
#define BFile_RecordNotFound -26
//#define BFile_NotDualRecordFile -27 // Not used
#define BFile_NoAlarmSupport -28
#define BFile_CannotAddAlarm -29
#define BFile_FileFindUsed -30
#define BFile_DeviceError -31
#define BFile_SystemNotLocked -32
#define BFile_DeviceNotFound -33
#define BFile_FileTypeMismatch -34
#define BFile_NotEmpty -35
#define BFile_BrokenSystemData -36
#define BFile_MediaNotReady -37
#define BFile_TooManyAlarms -38
#define BFile_SameAlarmExists -39
#define BFile_AccessSwapArea -40
#define BFile_MultimediaCard -41
#define BFile_CopyProtection -42
#define BFile_IllegalFileData -43
//---
// Search API
//
// The search API is described below. It's somewhat unreliable, with unclear
// semantics and a history of breaking in seemingly reasonable programs. One
// day probably we'll know how to use it properly and reliably.
//
// Note: always close search handles or trouble will ensue (eg. add-in
// discovery failing).
//---
@search FONTCHARACTER file path to match
@shandle Search handle to pass to BFile_FindNext or BFile_FindClose
@founfile FONTCHARACTER found file path
@fileinfo Structure containing a lot of information on the found file
Returns 0 on success, -1 if no file found, or negative value on error. */
struct BFile_FileInfo
{
uint16_t index;
@ -109,36 +234,59 @@ struct BFile_FileInfo
/* Address of first fragment (do not use directly) */
void *address;
};
enum BFile_FileType
{
BFile_Type_Directory = 0x0000,
BFile_Type_File = 0x0001,
BFile_Type_Addin = 0x0002,
BFile_Type_Eact = 0x0003,
BFile_Type_Language = 0x0004,
BFile_Type_Bitmap = 0x0005,
BFile_Type_MainMem = 0x0006,
BFile_Type_Temp = 0x0007,
BFile_Type_Dot = 0x0008,
BFile_Type_DotDot = 0x0009,
BFile_Type_Volume = 0x000a,
BFile_Type_Archived = 0x0041,
};
int BFile_FindFirst(uint16_t const *search, int *shandle, uint16_t *foundfile,
#define BFile_Type_Directory 0x0000
#define BFile_Type_File 0x0001
#define BFile_Type_Addin 0x0002
#define BFile_Type_Eact 0x0003
#define BFile_Type_Language 0x0004
#define BFile_Type_Bitmap 0x0005
#define BFile_Type_MainMem 0x0006
#define BFile_Type_Temp 0x0007
#define BFile_Type_Dot 0x0008
#define BFile_Type_DotDot 0x0009
#define BFile_Type_Volume 0x000a
#define BFile_Type_Archived 0x0041
/* BFile_FindFirst(): Search the storage memory for paths matching a pattern
This if for the storage memory only. There are only four search handles;
make sure to close them, there is no automatic device to close them for you
even after the add-in exists.
Search is NOT case sensitive. '*' can be used as a wildcard, although it's
unclear whether more than one '*' is supported, whether '*' can match in a
directory name, whether multiple folders can be searched simultaneously, and
whether directories can be matched.
@pattern FONTCHARACTER glob pattern
@shandle Will receive search handle (to use in BFile_FindNext/FindClose)
@foundfile Will receive FONTCHARACTER path of matching entry
@fileinfo Will receive metadata of matching entry
On success, returns 0, stores the search handle in *shandle, and stores
information about the first match in foundfile and fileinfo. The negative
error code BFile_EntryNotFound should be interpreted as an empty result (ie.
no entry matched) rather than an error.
Returns 0 on success or a negative error code. BFile_EntryNotFound should be
interpreted as an empty result (ie. no file matched). */
int BFile_FindFirst(uint16_t const *pattern, int *shandle, uint16_t *foundfile,
struct BFile_FileInfo *fileinfo);
/* BFile_FindNext(): Continue a search a directory for file with matching name
/* BFile_FindNext(): Continue a search
@shandle Search handle from BFile_FindFirst
@founfile FONTCHARACTER found file path
@fileinfo Structure containing a lot of information on the found file
Returns 0 on success, -1 if end is reached, or negative value on error. */
Continues the search for matches. The search handle is the value set in
*shandle in BFile_FindFirst(). As before, *foundfile receives the matching
entry's path and *fileinfo its metadata.
Returns 0 on success. The negative error code BFile_EnumerateEnd should be
interpreted as the end of the search (ie. all matching files have been
returned) rather than an error. */
int BFile_FindNext(int shandle, uint16_t *foundfile,
struct BFile_FileInfo *fileinfo);
/* BFile_FindClose(): Close the specified search handle
@shandle Search handle from BFile_FindFirst
Return 0 on success or negative value on error. */
/* Close a search handle (with or without exhausting the matches). */
int BFile_FindClose(int shandle);
#ifdef __cplusplus

View File

@ -23,6 +23,8 @@
.global _BFile_Open
.global _BFile_Close
.global _BFile_Size
.global _BFile_Seek
.global _BFile_GetPos
.global _BFile_Write
.global _BFile_Read
.global _BFile_FindFirst
@ -64,6 +66,7 @@ ___realloc:
/* BFile driver */
_BFile_Remove:
mov #0, r5
syscall(0x0439)
_BFile_Create:
syscall(0x0434)
@ -73,6 +76,11 @@ _BFile_Close:
syscall(0x042d)
_BFile_Size:
syscall(0x042f)
_BFile_Seek:
syscall(0x0431)
_BFile_GetPos:
rts
mov #-1, r0
_BFile_Write:
syscall(0x0435)
_BFile_Read:
@ -122,7 +130,6 @@ ___realloc:
/* BFile driver */
_BFile_Remove:
mov #0, r5
syscall(0x1db4)
_BFile_Create:
syscall(0x1dae)
@ -133,6 +140,10 @@ _BFile_Close:
syscall(0x1da4)
_BFile_Size:
syscall(0x1da6)
_BFile_Seek:
syscall(0x1da9)
_BFile_GetPos:
syscall(0x1dab)
_BFile_Write:
syscall(0x1daf)
_BFile_Read: