Last active
August 29, 2015 13:57
-
-
Save mazurio/9475271 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///////////////////////////////////////////////////////////////////////// | |
//// FAT_PIC.C //// | |
//// //// | |
//// Driver/Library for a FAT filesystem with a PIC //// | |
//// //// | |
//// This Library was designed to resemble standard ANSI C I/O as //// | |
//// much as possible. There are, however, some caveats to this. //// | |
//// Please read the comments to make sure the inputs and outputs //// | |
//// to each function are understood before using anything in //// | |
//// this library. //// | |
//// //// | |
//// This library supports FAT16 and FAT32, but not both at the same //// | |
//// time (this is a compile option, see options below). It is //// | |
//// recommended to use FAT32, FAT32 also has been tested more. //// | |
//// //// | |
//// Any function with an argument taking in a file name must be in //// | |
//// the form of... //// | |
//// "/filename.fil" for a file in the root directory //// | |
//// "/Directory/filename.fil" for a file in a subdirectory of root //// | |
//// "/Directory/Subdirectory/filename.fil" and so on... //// | |
//// //// | |
//// Any function with an argument taking in a directory name must //// | |
//// be in the form of... //// | |
//// "/Dirname/" for a directory in the root directory //// | |
//// "/Dirname/Subdirname/" for a directory in a subdirectory of //// | |
//// root and so on... //// | |
//// //// | |
//// A compatable media library must be provided. This is //// | |
//// documented after the User Functions. //// | |
//// //// | |
//// -- User Functions -- //// | |
//// //// | |
//// fat_init() //// | |
//// Initializes the FAT library, also initializes the media. //// | |
//// //// | |
//// fatopen(char *name, char *mode, FILE *fstream) //// | |
//// Opens up a FILE stream to a specified file with the specified //// | |
//// permission mode: //// | |
//// Permissions: "r" = read //// | |
//// "w" = write //// | |
//// "a" = append //// | |
//// "rb" = read binarily //// | |
//// "w" will erase all of the data in the file upon //// | |
//// the opening of the file. //// | |
//// "a" will tack on all of the data to the end of the //// | |
//// file. //// | |
//// "r" will keep on reading until the stream //// | |
//// hits an '\0' //// | |
//// "rb" will keep on reading until the amount of //// | |
//// bytes read equals the size of the file. //// | |
//// //// | |
//// Unlike standard C fopen(), this does not malloc a FILE - //// | |
//// instead the caller will have to have allready allocated a //// | |
//// a FILE and pass a pointer to it. //// | |
//// //// | |
//// fatreopen(char *name, char *mode, FILE *fstream) //// | |
//// Closes a FILE stream, then reopens the stream with a new file //// | |
//// and new permissions. //// | |
//// //// | |
//// fatclose(FILE *fstream) //// | |
//// Closes a FILE stream. It is very important to call this //// | |
//// function when you're done reading or writing to a file. //// //// | |
//// //// | |
//// fatgetc(FILE *fstream) //// | |
//// Gets a character from a stream. An EOF will be returned at //// | |
//// different times depending on whether or not the stream is //// | |
//// reading binarily. If not reading binarily: EOF when the //// | |
//// stream reads a '\0'. If reading binarily: EOF when the amount //// | |
//// of bytes read equals the size of the file (end of file). //// | |
//// //// | |
//// fatputc(char c, FILE *fstream) //// | |
//// Puts a character into a stream (write to the file). //// | |
//// Writes are buffered, so the media may not be written to until //// | |
//// a fatclose(). //// | |
//// //// | |
//// char* fatgets(char* str, int num, FILE *fstream) //// | |
//// Gets characters from a stream until either a '\r', EOF, or //// | |
//// num - 1 is hit. //// | |
//// //// | |
//// fatputs(char* str, FILE *fstream) //// | |
//// Puts a string into a stream (write a string to the file). //// | |
//// //// | |
//// fatprintf(FILE *stream): Printfs the entire stream. //// | |
//// printf()'s the entire stream (printf()'s the contents of the | |
//// file). | |
//// //// | |
//// fatgetpos(FILE *fstream, fatpos_t *pos) //// | |
//// Gets the current position of the stream/file, saves to pos. //// | |
//// //// | |
//// fatsetpos(FILE *fstream, fatpos_t *pos) //// | |
//// Sets the current position of the stream/file. //// | |
//// //// | |
//// fatseek(FILE *fstream, int32 offset, int origin) //// | |
//// Sets the current position of the stream according to the //// | |
//// origin parameter: //// | |
//// SEEK_CUR: Set position relative to the //// | |
//// current stream position. //// | |
//// SEEK_END: Set position relative to the //// | |
//// end of the stream. //// | |
//// SEEK_SET: Set position relative to the //// | |
//// beginning of the stream. //// | |
//// //// | |
//// fateof(FILE *fstream) //// | |
//// Returns non-zero if the stream/file position is at EOF, //// | |
//// non-zero if there are still data left in the stream. //// | |
//// //// | |
//// faterror(FILE *fstream): //// | |
//// Returns non-zero if there have been errors with the stream, //// | |
//// zero if the stream has been operating correctly since it has //// | |
//// been opened. //// | |
//// //// | |
//// fatread(void* buffer, int size, int32 num, FILE* fstream) //// | |
//// Reads size*num chars from the stream, saves to buffer. //// | |
//// //// | |
//// fatwrite(void* buffer, int size, int32 num, FILE* fstream) //// | |
//// Writes size*num chars from buffer to the stream. //// | |
//// //// | |
//// fatflush(FILE *fstream) //// | |
//// Flushes the buffer in a stream. //// | |
//// //// | |
//// clearerr(FILE *fstream) //// | |
//// Clears any error flags in the stream. //// | |
//// //// | |
//// rewind(FILE *fstream) //// | |
//// Send the stream back to the beginning of the file. //// | |
//// //// | |
//// fatpos_t fattell(FILE *fstream) //// | |
//// Returns the current position of the stream. //// | |
//// //// | |
//// rm_file(char *fname) //// | |
//// Removes a file. //// | |
//// //// | |
//// rm_dir(char *dirname) //// | |
//// Removes a directory. //// | |
//// //// | |
//// mk_file(char *fname) //// | |
//// Makes a file, file will be blank. //// | |
//// //// | |
//// mk_dir(char *dirname) //// | |
//// Makes a directory. //// | |
//// //// | |
//// format(int32 mediaSize) //// | |
//// Formats the media into a FAT32 or FAT16 file system. //// | |
//// If you specify a mediaSize larger than the actual media bad //// | |
//// things will happen. If you specify a mediaSize smaller than //// | |
//// the actual media size will simply limit the filesystem from //// | |
//// using 0 to mediaSize-1. Anything after mediaSize can be used //// | |
//// by the application (perhaps as a general purpose EEPROM?) //// | |
//// NOTE: Windows thinks the filesystem is RAW. //// | |
//// NOTE: This may be a little buggy. //// | |
//// //// | |
///////////////////////////////////////////////////////////////////////// | |
//// //// | |
//// This library was written to use CCS's MMC/SD library as the //// | |
//// media source. If you want to use a different media source, //// | |
//// you must provide the following 4 functions: //// | |
//// //// | |
//// int8 mmcsd_init(void); //// | |
//// Initializes the media. This will be called by fat_init(). //// | |
//// //// | |
//// int8 mmcsd_read_bytes(int32 a, int16 s, char *p); //// | |
//// Read s bytes from p to the media starting at address a. //// | |
//// //// | |
//// int8 mmcsd_write_data(int32 a, int16 s, char *p); //// | |
//// Write s bytes from p to the media starting at address a. //// | |
//// To maximize throughput on some medias, it's a good idea to //// | |
//// buffer writes in this function. //// | |
//// //// | |
//// int8 mmcsd_flush_buffer(void); //// | |
//// If your write function is buffering writes, this will flush //// | |
//// the buffer and write it to the media. //// | |
//// //// | |
//// All four functions should return 0 if OK, non-zero if error. //// | |
//// //// | |
///////////////////////////////////////////////////////////////////////// | |
//// (C) Copyright 2007 Custom Computer Services //// | |
//// This source code may only be used by licensed users of the CCS //// | |
//// C compiler. This source code may only be distributed to other //// | |
//// licensed users of the CCS C compiler. No other use, //// | |
//// reproduction or distribution is permitted without written //// | |
//// permission. Derivative programs created using this software //// | |
//// in object code form are not restricted in any way. //// | |
///////////////////////////////////////////////////////////////////////// | |
// NOTE This library has no concept of what time and date it currently is. | |
// All files and folders created or modified using this library | |
// will have invalid/inaccurate timestamps and datestamps. | |
// NOTE To save on ROM and RAM space, the user of this library will have to | |
// define what type of FAT they will be working with. The defines are | |
// in the Useful Defines section below. | |
// NOTE For faster writing or appending for an application such as a logger, | |
// uncomment #FAST_FAT below. This will make the FAT library assume | |
// there is one file on the card to write or append to, thereby | |
// making writing and appending much faster. Reading is impossible in | |
// this mode. | |
// THIS IS NOT TESTED VERY WELL YET! | |
// NOTE The current maximum file name length (full path) is 32 characters | |
// long. If longer file names are desired, change the | |
// MAX_FILE_NAME_LENGTH define below. Creating a file whose full path | |
// is longer than MAX_FILE_NAME_LENGTH may lead to weird operation. Keep | |
// in mind that making this define larger will make your RAM usage go | |
// up. | |
#ifndef FAT_PIC_C | |
#define FAT_PIC_C | |
#include <ctype.h> | |
#include <string.h> | |
#case | |
////////////////////// | |
/// /// | |
/// Useful Defines /// | |
/// /// | |
////////////////////// | |
/// Define your FAT type here /// | |
#define FAT16 | |
// #define FAT32 | |
/// For faster single-file writing, uncomment this line below /// | |
//#define FAST_FAT | |
/// Everything else /// | |
#define MAX_FILE_NAME_LENGTH 0x14 // the maximum length of a file name for our FAT, including /0 terminator | |
#define STREAM_BUF_SIZE 0x14 // how big the FILE buffer is. 0x20 is optimal | |
////////////////////////////////////////////////////////////////// | |
#define EOF -1 | |
#define GOODEC 0 | |
#define fatpos_t int32 | |
#define SEEK_CUR 0 | |
#define SEEK_END 1 | |
#define SEEK_SET 2 | |
//////////////////////// | |
/// /// | |
/// Global Variables /// | |
/// /// | |
//////////////////////// | |
int16 | |
Bytes_Per_Cluster, // number of addressable bytes per cluster | |
FAT_Start; // when the first FAT begins | |
int32 | |
Data_Start, // when data starts | |
FAT_Length, // the length of one FAT | |
Next_Free_Clust, // where the next free cluster is | |
Root_Dir; // when the root directory starts | |
enum filetype | |
{ | |
Data_File, // the stream is pointing to a binary, data file | |
Directory, // the stream is pointing to a directory | |
None // the stream isn't currently pointing to anything | |
}; | |
enum ioflags | |
{ | |
Closed = 0x00, | |
Read = 0x01, | |
Write = 0x02, | |
Append = 0x04, | |
Binary = 0x08, | |
EOF_Reached = 0x10, | |
Read_Error = 0x20, | |
Write_Error = 0x40, | |
File_Not_Found = 0x80 | |
}; | |
struct iobuf | |
{ | |
fatpos_t | |
Bytes_Until_EOF, // how many bytes until the stream's end of file | |
Cur_Char, // the current byte that the stream is pointing at | |
Entry_Addr, // the entry address of the file that is associated with the stream | |
Parent_Start_Addr, // the parent's start adddress of the file that is associated with the stream | |
Size, // the size of the file that is associated with the stream | |
Start_Addr; // the beginning of the data in the file that is associated with the stream | |
enum filetype File_Type; // the type of file that is associated with the stream | |
enum ioflags Flags; // any associated input/output flag | |
int Buf[STREAM_BUF_SIZE]; // this is a buffer so that during fatputc() or fatgetc() | |
// the media won't have to be read at every character | |
}; | |
typedef struct iobuf FILE; | |
/////////////////////////// | |
/// /// | |
/// Function Prototypes /// | |
/// /// | |
/////////////////////////// | |
/// Standard C Functions /// | |
signed int fatopen(char fname[], char mode[], FILE* stream); | |
signed int fatreopen(char fname[], char mode[], FILE* stream); | |
signed int fatclose(FILE* stream); | |
signed int fatgetc(FILE* stream); | |
signed int fatputc(int ch, FILE* stream); | |
char* fatgets(char* str, int num, FILE* stream); | |
signed int fatputs(char* str, FILE* stream); | |
signed int fatprintf(FILE* stream); | |
signed int fatgetpos(FILE* stream, fatpos_t* position); | |
signed int fatsetpos(FILE* stream, fatpos_t* position); | |
signed int fatseek(FILE* stream, int32 offset, int origin); | |
signed int fateof(FILE* stream); | |
signed int faterror(FILE* stream); | |
signed int fatread(void* buffer, int size, int32 num, FILE* stream); | |
signed int fatwrite(void* buffer, int size, int32 count, FILE* stream ); | |
signed int fatflush(FILE* stream); | |
signed int remove(char* fname); | |
void clearerr(FILE* stream); | |
void rewind(FILE* stream); | |
fatpos_t fattell(FILE* stream); | |
/// Non-Standard C Functions /// | |
// signed int rm_file(char fname[]); | |
// signed int rm_dir(char dname[]); | |
// signed int mk_file(char fname[]); | |
// signed int mk_dir(char dname[]); | |
/// Functions' Utility Functions /// | |
signed int set_file(char fname[], int attrib, FILE* stream); | |
signed int get_file_name(int32 file_entry_addr, char name[]); | |
signed int set_file_name(int32 parent_dir_addr, int32* entry_addr, char name[]); | |
signed int get_short_file_name(int32 file_entry_addr, char sname[], int type); | |
signed int make_short_file_name(int32 parent_dir_addr, char fname[], char sname[]); | |
int long_name_chksum (int* pFcbName); | |
signed int check_invalid_char(char fname[]); | |
#ifdef FAT32 | |
signed int get_next_free_cluster(int32* my_cluster); | |
signed int dealloc_clusters(int32 start_cluster); | |
signed int alloc_clusters(int32 start_cluster, int32* new_cluster_addr); | |
signed int clear_cluster(int32 cluster); | |
signed int write_fat(int32 cluster, int32 data); | |
#else // FAT16 | |
signed int get_next_free_cluster(int16* my_cluster); | |
signed int dealloc_clusters(int16 start_cluster); | |
signed int alloc_clusters(int16 start_cluster, int32* new_cluster_addr); | |
signed int clear_cluster(int16 cluster); | |
signed int write_fat(int16 cluster, int16 data); | |
#endif // #ifdef FAT32 | |
signed int get_next_file(FILE* stream); | |
signed int get_prev_file(FILE* stream); | |
signed int get_next_free_addr(int32* my_addr); | |
signed int get_next_free_entry(int32* start_addr); | |
signed int get_next_entry(int32* start_addr); | |
signed int get_prev_entry(int32* start_addr); | |
signed int read_buffer(FILE* stream, int* val); | |
signed int write_buffer(FILE* stream, int val); | |
void fill_entry(char the_entry[], char val, int8 start_ind); | |
void disp_timestamp(int16 timestamp); | |
void disp_datestamp(int16 datestamp); | |
/// Data Utility Functions /// | |
signed int fat_init(); | |
signed int get_next_cluster(int16* my_cluster); | |
signed int get_prev_cluster(int16* my_cluster); | |
int32 cluster_to_addr(int16 cluster); | |
int16 addr_to_cluster(int32 addr); | |
signed int get_next_addr(int32* my_addr); | |
signed int get_prev_addr(int32* my_addr); | |
signed int format(int32 DskSize); | |
/// Debugging Utility Functions /// | |
signed int disp_folder_contents(char foldername[]); | |
signed int dump_addr(int32 from, int32 to); | |
signed int dump_clusters(int32 from, int32 to); | |
void disp_fat_stats(); | |
signed int fatprintfinfo(FILE* stream); | |
//////////////////////////////// | |
/// /// | |
/// Function Implementations /// | |
/// /// | |
//////////////////////////////// | |
/// Standard C Functions /// | |
/* | |
signed int fatopen(char fname[], char mode[], FILE* stream) | |
Summary: This will open up a file stream for reading, writing, or appending. | |
Param fname: The full path of the file to open. | |
Param mode: The mode to open up the stream into. | |
"r" = Read | |
"w" = Write | |
"a" = Append | |
"rb", "wb", "ab" = Read, Write, or Append in Binary mode | |
Param stream: The stream to open up. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
Note: fname must be in the form of /filename.fil for a file in the root directory | |
/Directory/filename.fil for a file in a subdirectory of root | |
/Directory/Subdirectory/filename.fil and so on... | |
Note: Standard C will make a file in case a file isn't found, | |
however due to recursion this is not possible in CCSC. | |
*/ | |
signed int fatopen(char fname[], char mode[], FILE* stream) | |
{ | |
int fname_parse_pos = 1; // the current index of the fname character | |
char target_file[MAX_FILE_NAME_LENGTH]; // temporary buffer to hold names of files | |
FILE cur_stream; // this will be the stream that will be returned if all goes well | |
#ifndef FAST_FAT | |
int | |
depth = 0, // how many subdirectories deep the file is | |
target_file_parse_pos; // the current index of the target_file character | |
#endif // #ifndef FAST_FAT | |
// set flags | |
#ifdef FAST_FAT | |
switch(mode[0]) | |
{ | |
case 'w': | |
cur_stream.Flags = Write; | |
break; | |
case 'a': | |
cur_stream.Flags = Append; | |
break; | |
default: | |
return EOF; | |
} | |
// start looking for the file, start at root | |
cur_stream.Start_Addr = cur_stream.Parent_Start_Addr = Root_Dir; | |
while(fname[fname_parse_pos] != '\0') | |
{ | |
target_file[fname_parse_pos - 1] = fname[fname_parse_pos]; | |
fname_parse_pos += 1; | |
} | |
target_file[fname_parse_pos] = '\0'; | |
// find the file inside of its subdirectory | |
if(set_file(target_file, 0x20, &cur_stream) != GOODEC) | |
{ | |
cur_stream.Flags |= File_Not_Found; | |
*stream = cur_stream; | |
return EOF; | |
} | |
// at this point, we've found the file | |
*stream = cur_stream; | |
return GOODEC; | |
#else // NO FAST_FAT | |
switch(mode[0]) | |
{ | |
case 'r': | |
cur_stream.Flags = Read; | |
break; | |
case 'w': | |
cur_stream.Flags = Write; | |
break; | |
case 'a': | |
cur_stream.Flags = Append; | |
break; | |
default: | |
return EOF; | |
} | |
if(mode[1] == 'b') | |
cur_stream.Flags |= Binary; | |
// start looking for the file, start at root | |
cur_stream.Start_Addr = cur_stream.Parent_Start_Addr = Root_Dir; | |
// figure out how deep we have to go, count how many '/' we have in the string | |
while(fname[fname_parse_pos] != '\0') | |
{ | |
if(fname[fname_parse_pos] == '/') | |
depth++; | |
fname_parse_pos += 1; | |
} | |
// start the fname index at 1 to skip over the '/' | |
fname_parse_pos = 1; | |
// open up to the subdirectory, if possible | |
while(depth > 0) | |
{ | |
// find the name of our next target directory | |
target_file_parse_pos = 0; | |
while(fname[fname_parse_pos] != '/') | |
{ | |
// check to make sure that we're not at the end of a poorly formatted string | |
if(fname[fname_parse_pos] == '\0') | |
return EOF; | |
// fill up the buffer and increment the indexes | |
target_file[target_file_parse_pos] = fname[fname_parse_pos]; | |
fname_parse_pos += 1; | |
target_file_parse_pos += 1; | |
} | |
// increment the fname index one more because it's currently pointing at the '/' | |
fname_parse_pos += 1; | |
// tack on a \0 to the end of the target file to terminate the string | |
target_file[target_file_parse_pos] = '\0'; | |
// check to see if the directory exists and open it if possible, otherwise exit because the directory doesn't exist | |
if(set_file(target_file, 0x10, &cur_stream) != GOODEC) | |
{ | |
cur_stream.Flags |= File_Not_Found; | |
*stream = cur_stream; | |
return EOF; | |
} | |
depth -= 1; | |
} | |
// check to see if we're trying to open just a directory | |
if(fname[fname_parse_pos] == '\0') | |
{ | |
*stream = cur_stream; | |
return GOODEC; | |
} | |
// now that we have the location of the subdirectory that the file is in, attempt to open the file | |
target_file_parse_pos = 0; | |
while(fname[fname_parse_pos] != '\0') | |
{ | |
// fill up the buffer and increment the indexes | |
target_file[target_file_parse_pos] = fname[fname_parse_pos]; | |
fname_parse_pos += 1; | |
target_file_parse_pos += 1; | |
} | |
// tack on a \0 to the end of the target file to terminate the string | |
target_file[target_file_parse_pos] = '\0'; | |
// find the file inside of its subdirectory | |
if(set_file(target_file, 0x20, &cur_stream) != GOODEC) | |
{ | |
cur_stream.Flags |= File_Not_Found; | |
*stream = cur_stream; | |
return EOF; | |
} | |
// at this point, we've found the file | |
*stream = cur_stream; | |
return GOODEC; | |
#endif // #ifdef FAST_FAT | |
} | |
/* | |
signed int fatreopen(char fname[], char mode[], FILE* old_stream, FILE* new_stream) | |
Summary: This will close a stream and then reopen it using new parameters. | |
Param fname: The full path of the file to open. | |
Param mode: The mode to open up the stream into. | |
"r" = Read | |
"w" = Write | |
"a" = Append | |
"rb", "wb", "ab" = Read, Write, or Append in Binary mode | |
Param stream: The stream to close and reopen. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
Note: fname must be in the form of /filename.fil for a file in the root directory | |
/Directory/filename.fil for a file in a subdirectory of root | |
/Directory/Subdirectory/filename.fil and so on... | |
Note: Standard C will make a file in case a file isn't found, | |
however due to recursion this is not possible in CCSC. | |
*/ | |
signed int fatreopen(char fname[], char mode[], FILE* stream) | |
{ | |
// close the old stream | |
if(fatclose(stream) == EOF) | |
return EOF; | |
// open the new stream | |
if(fatopen(fname, mode, stream) == EOF) | |
return EOF; | |
return GOODEC; | |
} | |
/* | |
signed int fatclose(FILE* stream) | |
Summary: Closes a stream and commits any changes done to the file. | |
Param: The stream to close. | |
Returns: EOF if there was a problem, 0 if everything went okay. | |
*/ | |
signed int fatclose(FILE* stream) | |
{ | |
int ec = 0; | |
int32 first_cluster; | |
// commit data back to the stream's entry, if needed | |
if((stream->Flags & Write) || (stream->Flags & Append)) | |
{ | |
// write the new size of the file | |
if(mmcsd_write_data(stream->Entry_Addr + 0x1C, 4, &(stream->Size)) != GOODEC) | |
{ | |
stream->Flags |= Write_Error; | |
return EOF; | |
} | |
// check to see if the first cluster is already linked in the file | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x14, 2, (int16*)&first_cluster + 1); | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x1A, 2, &first_cluster); | |
if(ec != GOODEC) | |
{ | |
stream->Flags |= Read_Error; | |
return EOF; | |
} | |
// write the first cluster to the entry if needed | |
if(first_cluster == 0) | |
{ | |
// convert the start address to a cluster number | |
first_cluster = addr_to_cluster(stream->Start_Addr); | |
ec += mmcsd_write_data(stream->Entry_Addr + 0x14, 2, (int16*)&first_cluster + 1); | |
ec += mmcsd_write_data(stream->Entry_Addr + 0x1A, 2, &first_cluster); | |
if(ec != GOODEC) | |
{ | |
stream->Flags |= Write_Error; | |
return EOF; | |
} | |
} | |
// dump the remaining buffer to the card | |
if(fatflush(stream) == EOF) | |
return EOF; | |
} | |
// nullify the data | |
stream->Cur_Char = 0; | |
stream->Entry_Addr = 0; | |
stream->Size = 0; | |
stream->Start_Addr = 0; | |
stream->Flags = 0; | |
return 0; | |
} | |
/* | |
signed int fatgetc(FILE* stream) | |
Summary: Gets a character from a stream. | |
Param: The stream to get a character from. | |
Returns: The character that was gotten from the stream, | |
EOF if the stream has reached the end of the file or doesn't have permissions to read, | |
*/ | |
signed int fatgetc(FILE* stream) | |
{ | |
char ch; // character read in | |
// check to see if the stream has proper permissions to read | |
if(stream->Flags & Read) | |
{ | |
// when the number of bytes until eof hit zero, we know we are at the end of any file | |
if(stream->Bytes_Until_EOF == 0) | |
{ | |
stream->Flags |= EOF_Reached; | |
return EOF; | |
} | |
// read in the next byte in the buffer | |
if(read_buffer(stream, &ch) == EOF) | |
return EOF; | |
// a 0x00 will signify the end of a non-binary file | |
if((ch == '\0') && !(stream->Flags & Binary)) | |
{ | |
stream->Flags |= EOF_Reached; | |
return EOF; | |
} | |
// get the next contiguous address of the stream | |
if(get_next_addr(&(stream->Cur_Char)) != GOODEC) | |
return EOF; | |
// we just got 1 byte closer to the end of the file | |
stream->Bytes_Until_EOF -= 1; | |
return ch; | |
} | |
// if the stream doesn't have proper permissions to read, return an EOF | |
else | |
return EOF; | |
} | |
/* | |
signed int fatputc(int ch, FILE* stream) | |
Summary: Puts a character into a stream. | |
Param ch: The character to put into the stream. | |
Param stream: The stream to put a character into. | |
Returns: The character that was put into the stream, | |
EOF if the stream doesn't have permissions to write, or if a problem happened. | |
*/ | |
signed int fatputc(int ch, FILE* stream) | |
{ | |
// check to see if the stream has proper permissions to write | |
if(((stream->Flags & Write) || (stream->Flags & Append)) && (stream->File_Type == Data_File)) | |
{ | |
// if there isn't any space allocated yet, allocate some | |
if(stream->Cur_Char < Data_Start) | |
{ | |
if(get_next_free_cluster(&Next_Free_Clust) == EOF) | |
return EOF; | |
#ifdef FAT32 | |
if(write_fat(Next_Free_Clust, 0x0FFFFFFF) == EOF) | |
return EOF; | |
#else // FAT16 | |
if(write_fat(Next_Free_Clust, 0xFFFF) == EOF) | |
return EOF; | |
#endif // #ifdef FAT32 | |
if(clear_cluster(Next_Free_Clust) == EOF) | |
return EOF; | |
stream->Cur_Char = stream->Start_Addr = cluster_to_addr(Next_Free_Clust); | |
} | |
// write the next character to the buffer | |
if(write_buffer(stream, ch) == EOF) | |
return EOF; | |
// get the next address, increment Cur_Char | |
if(get_next_addr(&(stream->Cur_Char)) == EOF) | |
{ | |
// write the current buffer to the end of the current cluster | |
if(mmcsd_write_data(stream->Cur_Char - STREAM_BUF_SIZE + 1, STREAM_BUF_SIZE, stream->Buf) != GOODEC) | |
{ | |
stream->Flags |= Write_Error; | |
return EOF; | |
} | |
// start looking for a new cluster to allocate | |
if(alloc_clusters(addr_to_cluster(stream->Cur_Char), &(stream->Cur_Char)) == EOF) | |
return EOF; | |
} | |
// our file just got bigger by 1 byte | |
stream->Size += 1; | |
return ch; | |
} | |
// if the stream doesn't have proper permissions to write, return an EOF | |
else | |
return EOF; | |
} | |
/* | |
char* fatgets(char* str, int num, FILE* stream) | |
Summary: Reads characters from a stream into a string. | |
Param str: A pointer to the beginning of the string to put characters into. | |
Param num: The number of characters to put into the string - 1. | |
Param stream: The stream to read from. | |
Returns: A pointer to the most recently added character, or NULL if there was an error. | |
Note: If a newline is read from the stream, then str will be terminated with a newline. | |
If num - 1 or EOF is reached, then str will be null terminated. | |
*/ | |
char* fatgets(char* str, int num, FILE* stream) | |
{ | |
int i; // counter for loops | |
// loop until num - 1 | |
for(i = 0; i < num - 1; i += 1) | |
{ | |
str[i] = fatgetc(stream); | |
if(str[i] == '\n') | |
return str; | |
if(str[i] == EOF) | |
break; | |
} | |
// close off str with a null terminator | |
str[i] = '\0'; | |
return str; | |
} | |
/* | |
signed int fatputs(char* str, FILE* stream) | |
Summary: Writes characters from a string into a stream. | |
Param str: A pointer to the beginning of the string to write into the stream. | |
Param stream: The stream to write into. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
*/ | |
signed int fatputs(char* str, FILE* stream) | |
{ | |
int i = 0; // counter for loops | |
// fatputc every character in the stream | |
while(str[i] != '\0') | |
{ | |
if(fatputc(str[i], stream) == EOF) | |
return EOF; | |
i += 1; | |
} | |
return GOODEC; | |
} | |
/* | |
signed int fatprintf(FILE* stream) | |
Summary: This will print off the entire contents of the stream to the console. | |
Param: The stream to print off. | |
Returns: The last character printed off to the console. | |
*/ | |
signed int fatprintf(FILE* stream) | |
{ | |
signed int ch; // character read in | |
// keep on printf any characters read in as long as we don't run into an end of file or a media error | |
do | |
{ | |
ch = fatgetc(stream); | |
printf("%c", ch); | |
} while(ch != EOF); | |
return ch; | |
} | |
/* | |
signed int fatgetpos(FILE* stream, fatpos_t* position) | |
Summary: Returns the current position of where the stream is pointing to relative to the beginning of the stream. | |
Param stream: The stream to get the position of. | |
Param position: A pointer to a variable put the current position of the pointer into. | |
Returns: 0 on success. | |
*/ | |
signed int fatgetpos(FILE* stream, fatpos_t* position) | |
{ | |
*position = stream->Size - stream->Bytes_Until_EOF; | |
return 0; | |
} | |
/* | |
signed int fatsetpos(FILE* stream, fatpos_t* position) | |
Summary: Sets the current position of where the stream is pointing to in memory relative to the beginning of the stream. | |
Param stream: The stream to set the position of. | |
Param position: A pointer the a variable that has the value of the new position. | |
Returns: 0 on success, or EOF if there was error. | |
*/ | |
signed int fatsetpos(FILE* stream, fatpos_t* position) | |
{ | |
#ifndef FAST_FAT | |
#ifdef FAT32 | |
int32 cur_cluster; // the current cluster we're pointing to | |
#else // FAT16 | |
int16 cur_cluster; // the current cluster we're pointing to | |
#endif // #ifdef FAT32 | |
int32 i; // pointer to memory | |
#endif // #ifndef FAST_FAT | |
// check to see if we want to just rewind the file | |
if(*position == 0) | |
{ | |
rewind(stream); | |
return GOODEC; | |
} | |
// this whole process is much different and easier if we're writing or appending at a spot after EOF | |
// this will essentially write null characters to the file from EOF to the desired position | |
if(((stream->Flags & Write) || (stream->Flags & Append)) && (stream->Size < *position)) | |
{ | |
while(stream->Size < *position) | |
if(fatputc('\0', stream) == EOF) | |
return EOF; | |
return 0; | |
} | |
#ifdef FAST_FAT | |
stream->Cur_Char = stream->Start_Addr + *position; | |
#else // NO FAST_FAT | |
// figure out how many clusters into the file the position is to be set to | |
i = *position / Bytes_Per_Cluster; | |
cur_cluster = addr_to_cluster(stream->Start_Addr); | |
// head to that cluster | |
while(i > 0) | |
{ | |
if(get_next_cluster(&cur_cluster) != GOODEC) | |
return EOF; | |
i -= 1; | |
} | |
// head to the correct cluster | |
stream->Cur_Char = cluster_to_addr(cur_cluster); | |
// now that we're in the correct cluster, tack on the remaining position | |
stream->Cur_Char += (*position % Bytes_Per_Cluster); | |
if(stream->Flags & Read) | |
{ | |
// we now need to change how far it is until EOF | |
stream->Bytes_Until_EOF = stream->Size - *position; | |
// fill up the buffer | |
if(mmcsd_read_data(stream->Cur_Char, STREAM_BUF_SIZE, stream->Buf) != GOODEC) | |
{ | |
stream->Flags |= Read_Error; | |
return EOF; | |
} | |
} | |
else | |
stream->Size = *position; | |
#endif // #ifdef FAST_FAT | |
return 0; | |
} | |
/* | |
signed int fatseek(FILE* stream, int32 offset, int origin) | |
Summary: This will set the position of the file stream according to the input. The EOF flag will also be cleared. | |
Param stream: The stream to set the position of. | |
Param offset: How many bytes relative of origin the file stream position will be set. | |
Param origin: This will be one of 3 values... | |
SEEK_CUR: Set position relative to the current stream position. | |
SEEK_END: Set position relative to the end of the stream. | |
SEEK_SET: Set position relative to the beginning of the stream. | |
Returns: 0 on success, or EOF if there was error. | |
*/ | |
signed int fatseek(FILE* stream, int32 offset, int origin) | |
{ | |
int32 myoffset; // since fatsetpos requires a pointer to a variable, we need this here | |
switch(origin) | |
{ | |
case SEEK_CUR: | |
myoffset = stream->Cur_Char + offset; | |
if(fatsetpos(stream, &myoffset) != 0) | |
return EOF; | |
break; | |
case SEEK_END: | |
myoffset = stream->Size - offset; | |
if(fatsetpos(stream, &myoffset) != 0) | |
return EOF; | |
break; | |
case SEEK_SET: | |
myoffset = offset; | |
if(fatsetpos(stream, &myoffset) != 0) | |
return EOF; | |
break; | |
default: | |
return EOF; | |
} | |
// clear the EOF flag | |
stream->Flags &= 0xEF; | |
return GOODEC; | |
} | |
/* | |
signed int fateof(FILE* stream) | |
Summary: Determines whether or not the stream is at the end of the file. | |
Param: The stream to query for EOF. | |
Returns: A non-zero value if the file is at EOF, | |
0 if the file is not at EOF. | |
*/ | |
signed int fateof(FILE* stream) | |
{ | |
return stream->Flags & EOF_Reached; | |
} | |
/* | |
signed int fatread(void* buffer, int size, int32 num, FILE* stream) | |
Summary: Fills up an array with data from a stream. | |
Param buffer: A pointer to the beginning of an array of any type. | |
Param size: How many bytes each element in the array is. | |
Param num: How many elements to fill in the array. | |
Param stream: The stream to read from. | |
Returns: How many values were written to the array. | |
*/ | |
signed int fatread(void* buffer, int size, int32 num, FILE* stream) | |
{ | |
int32 i; // counter for loop | |
// fill up every byte | |
for(i = 0; i < (num * size); i += 1) | |
buffer[i] = fatgetc(stream); | |
return i; | |
} | |
/* | |
signed int fatwrite(void* buffer, int size, int32 count, FILE* stream ) | |
Summary: Fills up a stream with data from an array | |
Param buffer: A pointer to the beginning of an array of any type. | |
Param size: How many bytes each element in the array is. | |
Param num: How many elements to write to the stream. | |
Param stream: The stream to write to. | |
Returns: How many values were written to the stream. | |
*/ | |
signed int fatwrite(void* buffer, int size, int32 count, FILE* stream ) | |
{ | |
int32 i; // counter for loop | |
// write every byte | |
for(i = 0; i < (count * (int32)size); i += 1) | |
if(fatputc(buffer[i], stream) == EOF) | |
return EOF; | |
return i; | |
} | |
/* | |
signed int fatflush(FILE* stream) | |
Summary: Flushes the buffer of a given stream. | |
Param: The stream to flush the buffer of. | |
Returns: EOF if there was a problem, 0 if everything went okay | |
*/ | |
signed int fatflush(FILE* stream) | |
{ | |
// check to see if we have a buffer | |
if((stream->Flags & Write) || (stream->Flags & Append)) | |
{ | |
// check to see if we need to flush the buffer | |
if(stream->Cur_Char % STREAM_BUF_SIZE == 0) | |
{ | |
// flush the buffer to the card | |
if(mmcsd_write_data(stream->Cur_Char - STREAM_BUF_SIZE, STREAM_BUF_SIZE, stream->Buf) != GOODEC) | |
{ | |
stream->Flags |= Write_Error; | |
return EOF; | |
} | |
} | |
else | |
{ | |
// flush the buffer to the card | |
// we need to make sure that the buffer gets flushed into the proper location, hence all this weird % math | |
if(mmcsd_write_data(stream->Cur_Char - (stream->Cur_Char % STREAM_BUF_SIZE), STREAM_BUF_SIZE, stream->Buf) != GOODEC) | |
{ | |
stream->Flags |= Write_Error; | |
return EOF; | |
} | |
} | |
return(mmcsd_flush_buffer()); | |
} | |
return 0; | |
} | |
/* | |
signed int faterror(FILE* stream) | |
Summary: Checks for an error in a given stream. | |
Param: The stream to check for an error in. | |
Returns: A non-zero value of there is an error in the stream, | |
0 if there is no error in the stream | |
*/ | |
signed int faterror(FILE* stream) | |
{ | |
return stream->Flags & 0xF0; | |
} | |
/* | |
void clearerr(FILE* stream) | |
Summary: Clears off all error in a given stream. | |
Param: The stream to clear off the errors in. | |
Returns: Nothing. | |
*/ | |
void clearerr(FILE* stream) | |
{ | |
stream->Flags &= 0x0F; | |
} | |
/* | |
void rewind(FILE* stream) | |
Summary: Sets the stream to point back to the beginning of a file. | |
Param: The stream to rewind. | |
Returns: Nothing. | |
*/ | |
void rewind(FILE* stream) | |
{ | |
// set the stream back to the beginning | |
stream->Cur_Char = stream->Start_Addr; | |
stream->Bytes_Until_EOF = stream->Size; | |
} | |
/* | |
fatpos_t fattell(FILE* stream) | |
Summary: Returns the current position of where the stream is pointing to relative to the beginning of the stream. | |
Param: The stream to return the position of. | |
Returns: The position of where the stream is pointing to relative to the beginning of the stream, or 0 if there was a problem. | |
*/ | |
fatpos_t fattell(FILE* stream) | |
{ | |
fatpos_t retval; | |
if(fatgetpos(stream, &retval) != 0) | |
return 0; | |
return retval; | |
} | |
/// Functions' Utility Functions /// | |
/// NOTE: A library user should not need to use any of the functions in this section /// | |
/* | |
signed int set_file(char fname[], int attrib, FILE* stream) | |
Summary: This will set the stream to point to the specified file. | |
Param fname: The file name to search for. | |
Param attrib: The file attributes to search for. 0x10 is a directory, 0x20 is a file. | |
Param stream: The stream to set. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
Note: The stream has to be pointing to the parent directory's start address when coming in to this function. | |
*/ | |
signed int set_file(char fname[], int attrib, FILE* stream) | |
{ | |
int | |
cur_attrib, // the attribute of the most recently read entry | |
cur_state, // the state of the most recently read entry | |
ec = 0; // error checking byte | |
int32 i; // pointer to memory | |
#ifndef FAST_FAT | |
char name_buffer[MAX_FILE_NAME_LENGTH]; // buffer to hold in the most recently read in name | |
#endif // #ifndef FAST_FAT | |
// set the memory pointer to the parent start address | |
i = stream->Start_Addr; | |
// search for the name of our target file inside of the parent directory | |
do | |
{ | |
// read the state and the attribute of the current entry | |
ec += mmcsd_read_data(i, 1, &cur_state); | |
ec += mmcsd_read_data(i + 0x0B, 1, &cur_attrib); | |
if(ec != GOODEC) | |
{ | |
stream->Flags |= Read_Error; | |
return EOF; | |
} | |
// check to make sure that this entry exists and the entry is the type we're looking for | |
if((cur_state != 0xE5) && (cur_attrib == attrib)) | |
{ | |
#ifndef FAST_FAT | |
// get the long file name of the current entry | |
if(get_file_name(i, name_buffer) == EOF) | |
return EOF; | |
// if the target entry and the long file name are equal, strcmp will return a zero | |
if(strcmp(fname, name_buffer) == 0) | |
#endif // #ifndef FAST_FAT | |
{ | |
// we have found our target entry, set the current entry and break out of this function | |
// set stream's parent address | |
stream->Parent_Start_Addr = stream->Start_Addr; | |
ec += mmcsd_read_data(i + 0x1C, 4, &(stream->Size)); | |
// stream->Start_Addr is going to temporarily have a cluster number | |
ec += mmcsd_read_data(i + 0x14, 2, (int16*)&stream->Start_Addr + 1); | |
ec += mmcsd_read_data(i + 0x1A, 2, &stream->Start_Addr); | |
if(ec != GOODEC) | |
{ | |
stream->Flags |= Read_Error; | |
return EOF; | |
} | |
// convert stream->Start_Addr to an address | |
stream->Start_Addr = cluster_to_addr(stream->Start_Addr); | |
stream->Entry_Addr = i; | |
stream->Bytes_Until_EOF = stream->Size; | |
// set up some permission-specific parameters if we're at a file | |
if(attrib == 0x20) | |
{ | |
stream->File_Type = Data_File; | |
if(stream->Flags & Write) | |
{ | |
// delete all previous data in the file | |
stream->Bytes_Until_EOF = stream->Size = 0; | |
// if there is already space allocated, get rid of it | |
if(stream->Start_Addr >= Data_Start) | |
if(dealloc_clusters(addr_to_cluster(stream->Start_Addr)) == EOF) | |
return EOF; | |
stream->Cur_Char = 0; | |
} | |
if((stream->Flags & Append) && (stream->Size != 0)) | |
{ | |
// set the position to the end of the file and fill the buffer with the contents of the end of the file | |
ec = fatsetpos(stream, &(stream->Size)); | |
if(stream->Cur_Char % STREAM_BUF_SIZE == 0) | |
ec += mmcsd_read_data(stream->Cur_Char - STREAM_BUF_SIZE, STREAM_BUF_SIZE, stream->Buf); | |
else | |
ec += mmcsd_read_data(stream->Cur_Char - (stream->Cur_Char % STREAM_BUF_SIZE), STREAM_BUF_SIZE, stream->Buf); | |
} | |
#ifndef FAST_FAT | |
if(stream->Flags & Read) | |
{ | |
stream->Cur_Char = stream->Start_Addr; | |
// fill up the read buffer for reading | |
ec = mmcsd_read_data(stream->Cur_Char, STREAM_BUF_SIZE, stream->Buf); | |
} | |
#endif // #ifndef FAST_FAT | |
if(ec != GOODEC) | |
{ | |
stream->Flags |= Read_Error; | |
return EOF; | |
} | |
} | |
else | |
stream->File_Type = Directory; | |
return GOODEC; | |
} | |
} | |
// check to make sure that the next iteration will give us a contiguous cluster | |
if(get_next_entry(&i) == EOF) | |
return EOF; | |
} while(cur_state != 0x00); | |
// if we reach this point, we know that the file won't be found | |
stream->Flags |= File_Not_Found; | |
return EOF; | |
} | |
/* | |
signed int get_file_name(int32 file_entry_addr, char name[]) | |
Summary: This will get a name of a file. | |
Param file_entry_addr: The position in memory that the file's entry is. | |
Param name[]: The place to put the name of the file into. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
*/ | |
signed int get_file_name(int32 file_entry_addr, char name[]) | |
{ | |
int | |
j, // counter for loops | |
k = 0, // current character in array | |
order, // byte to hold the current long file name order | |
type; // the type of entry that was just read in | |
int32 i; // pointer for memory | |
// the long file name isn't fragmented across clusters | |
i = file_entry_addr; | |
// check to make sure that this file has a long file name | |
if(mmcsd_read_data(i - 0x15, 1, &type) != GOODEC) | |
return EOF; | |
if(type != 0x0F) | |
{ | |
// this file doesn't have a long file name | |
if(get_short_file_name(i, name, type) == EOF) | |
return EOF; | |
return GOODEC; | |
} | |
do | |
{ | |
// head to the previous entry | |
if(get_prev_entry(&i) == EOF) | |
return EOF; | |
for(j = 1; j < 0x20; j += 2, k += 1) | |
{ | |
if(j == 11) | |
j = 14; | |
else if(j == 26) | |
j = 28; | |
if(mmcsd_read_data(j + i, 1, &(name[k])) != GOODEC) | |
return EOF; | |
} | |
// now that that's done with, get the entry's order | |
if(mmcsd_read_data(i, 1, &order) != GOODEC) | |
return EOF; | |
} while(!(order & 0x40)); // the last entry will be 0b01xxxxxx | |
// end the name[] buffer with a \0 | |
name[k] = '\0'; | |
return GOODEC; | |
} | |
/* | |
signed int set_file_name(int32 parent_dir_addr, int32 entry_addr, char name[]) | |
Summary: Creates both a short and long file name at the first free entry in a directory. | |
Param parent_dir_addr: The address of the parent directory to create the short file name in. | |
Param entry_addr: The address the function put the short file name entry. | |
Param name: The full file name. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
*/ | |
signed int set_file_name(int32 parent_dir_addr, int32* entry_addr, char name[]) | |
{ | |
char sname[12]; // place to hold the short file name | |
signed int name_pos = 0; // the current parse position of name[] | |
int | |
chksum, // the long file name checksum | |
entire_entry[0x20], // the entire entry to put write onto the media | |
entry_pos, // the current position inside of entire_entry | |
long_entry = 1; // the current long entry number we're on | |
int32 i; // pointer to memory | |
// check for invalid characters | |
if(check_invalid_char(name) == EOF) | |
return EOF; | |
// make a short file name of this | |
if(make_short_file_name(parent_dir_addr, name, sname) == EOF) | |
return EOF; | |
// get a checksum for the long file name entries | |
chksum = long_name_chksum(sname); | |
// start writing the long file name | |
// zero out entry[] | |
for(entry_pos = 0; entry_pos < 0x20; entry_pos += 1) | |
entire_entry[entry_pos] = 0; | |
i = parent_dir_addr; | |
if(get_next_free_entry(&i) == EOF) | |
return EOF; | |
// 0x0F signifies an file entry | |
entire_entry[11] = 0x0F; | |
// since we're working in reverse, write the final long name entry | |
name_pos = strlen(name); | |
name_pos %= 13; | |
if(name_pos != 0) | |
{ | |
// add 1 to account for the \0 terminator | |
name_pos += 1; | |
fill_entry(entire_entry, 0xFF, name_pos); | |
} | |
// start writing the long file name entries | |
name_pos = strlen(name); | |
// figure out how many entries this name will take up | |
long_entry = (name_pos / 13) + 1; | |
if(name_pos % 13 == 0) | |
long_entry -= 1; | |
// set the bit to signify this is the final entry | |
long_entry |= 0x40; | |
while(name_pos >= 0) | |
{ | |
entry_pos = name_pos % 13; | |
if(entry_pos < 5) | |
entire_entry[(entry_pos << 1) + 1] = name[name_pos]; | |
else if(entry_pos < 11) | |
entire_entry[(entry_pos << 1) + 4] = name[name_pos]; | |
else | |
entire_entry[(entry_pos << 1) + 6] = name[name_pos]; | |
if((entry_pos == 0) | |
&& (name_pos != strlen (name))) | |
{ | |
entire_entry[0] = long_entry; | |
// clear off the bits at positions 6 and 7 if the most recent entry was the final one. | |
if(name_pos != 0) | |
long_entry &= 0x3F; | |
long_entry -= 1; | |
entire_entry[13] = chksum; | |
if(mmcsd_write_data(i, 0x20, entire_entry) != GOODEC) | |
return EOF; | |
if(get_next_free_entry(&i) == EOF) | |
return EOF; | |
fill_entry(entire_entry, 0x00, 0); | |
} | |
name_pos -= 1; | |
} | |
// write the short file name to the entry | |
if(mmcsd_write_data(i, 11, sname) != GOODEC) | |
return EOF; | |
// set the new entry addr | |
*entry_addr = i; | |
return GOODEC; | |
} | |
/* | |
signed int get_short_file_name(int32 file_entry_addr, char sname[], int type) | |
Summary: Reads a file's short file name, puts all the characters into lower case, and puts it into a buffer. | |
Param file_entry_addr: Where the file's entry address is located. | |
Param sname: The buffer to put the short file name. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
*/ | |
signed int get_short_file_name(int32 file_entry_addr, char sname[], int8 type) | |
{ | |
int | |
buf, | |
i, | |
j = 0; | |
// one short file name has, at the most, 11 characters | |
for(i = 0; i < 11; ++i) | |
{ | |
// read in a character | |
if(mmcsd_read_data(i + file_entry_addr, 1, &buf) != GOODEC) | |
return EOF; | |
// convert the character | |
if(buf != ' ') | |
{ | |
if (i == 8 && type != 0x10) sname[j++] = '.'; | |
sname[j++] = tolower(buf); | |
} | |
} | |
if (sname[j - 1] == '.') --j; | |
sname[j] = '\0'; | |
return GOODEC; | |
} | |
/* | |
signed int make_short_file_name(int32 parent_dir_addr, char fname[], char sname[]) | |
Summary: Creates a unique short file name in a directory. | |
Param parent_dir_addr: The address of the parent directory to create the short file name in. | |
Param fname: The full file name. | |
Param sname: Character array that will hold the short file name upon function completion. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
*/ | |
signed int make_short_file_name(int32 parent_dir_addr, char fname[], char sname[]) | |
{ | |
char | |
val[12] = " ", | |
cur_fname[12] = " ", | |
cur_fnum[7] = " "; | |
int | |
buf, | |
ext_pos, | |
fname_parse_pos = 0, | |
val_parse_pos = 0; | |
int32 | |
fnum = 0, | |
i; | |
// figure out where the extension position is | |
ext_pos = strchr(fname, '.'); | |
// check to see if this file has an extension | |
if(ext_pos == 0) | |
{ | |
while((val_parse_pos < 8) && (fname[fname_parse_pos] != '\0')) | |
{ | |
val[val_parse_pos] = toupper(fname[fname_parse_pos]); | |
val_parse_pos += 1; | |
fname_parse_pos += 1; | |
// we can't have a '.' or ' ' in the short name | |
while((fname[fname_parse_pos] == '.') || (fname[fname_parse_pos] == ' ')) | |
fname_parse_pos += 1; | |
} | |
} | |
else | |
{ | |
ext_pos -= fname - 1; | |
while((val_parse_pos < 11) && (fname[fname_parse_pos] != '\0')) | |
{ | |
val[val_parse_pos] = toupper(fname[fname_parse_pos]); | |
val_parse_pos += 1; | |
fname_parse_pos += 1; | |
// we can't have a '.' or ' ' in the short name | |
while((fname[fname_parse_pos] == '.') || (fname[fname_parse_pos] == ' ')) | |
fname_parse_pos += 1; | |
// check to see if it's time to skip val_parse_pos ahead to the file extension | |
if(fname_parse_pos == ext_pos) | |
val_parse_pos = 8; | |
// check to see if it's time to skip name_parse_pos ahead to the file extension | |
else if(val_parse_pos == 8) | |
fname_parse_pos = ext_pos; | |
} | |
} | |
// now that we've got the short file name, we need to make it unique | |
i = parent_dir_addr; | |
if(mmcsd_read_data(i + 0x0B, 1, &buf) != GOODEC) | |
return EOF; | |
// keep reading until we hit empty space | |
while(buf != 0x00) | |
{ | |
// check to see if this is a short file name entry | |
if((buf == 0x20) || (buf == 0x10)) | |
{ | |
// read in the short file name that we're currently pointing at | |
if(mmcsd_read_data(i, 11, cur_fname) != GOODEC) | |
return EOF; | |
cur_fname[11] = '\0'; | |
// strcmp will return a 0 if the file name we're currently pointing at and the file name that we created above are the same | |
if(strcmp(cur_fname, val) == 0) | |
{ | |
// we now need to create a unique file name | |
// increment the unique file number by one | |
fnum += 1; | |
// convert the unique file number to a string | |
sprintf(cur_fnum, "%lu", fnum); | |
// put the unique file number, along with a '~' into our short file name | |
fname_parse_pos = 0; | |
// find out the last posiiton of a space | |
val_parse_pos = strchr(val, ' '); | |
if(val_parse_pos == 0) | |
// if there isn't a space, then we're going to have to put the ~x at the end of the short name | |
val_parse_pos = 7 - strlen(cur_fnum); | |
else | |
// if there is a space, then put the ~x there | |
val_parse_pos -= val + 2; | |
// make some room for extra digits | |
buf = 10; | |
while(fnum >= buf) | |
{ | |
val_parse_pos -= 1; | |
buf *= 10; | |
} | |
// write in the ~ | |
val[val_parse_pos] = '~'; | |
// write in the number | |
val_parse_pos += 1; | |
while(cur_fnum[fname_parse_pos] != '\0') | |
{ | |
val[val_parse_pos] = cur_fnum[fname_parse_pos]; | |
val_parse_pos += 1; | |
fname_parse_pos += 1; | |
} | |
// start the search over again to see if that unique file name/number combination is still taken up | |
i = parent_dir_addr; | |
} | |
} | |
// head to the next entry | |
if(get_next_entry(&i) == EOF) | |
{ | |
// we're going to have to allocate another cluster | |
if(alloc_clusters(addr_to_cluster(i), &i) == EOF) | |
return EOF; | |
} | |
if(mmcsd_read_data(i + 0x0B, 1, &buf) != GOODEC) | |
return EOF; | |
} | |
// copy the short name into the input buffer | |
for(i = 0; i < 12; i += 1) | |
sname[i] = val[i]; | |
return GOODEC; | |
} | |
/* | |
int long_name_chksum (int* FcbName) | |
Summary: Returns an unsigned byte checksum computed on an unsigned byte | |
array. The array must be 11 bytes long and is assumed to contain | |
a name stored in the format of a MS-DOS directory entry. | |
Param: Pointer to an unsigned byte array assumed to be 11 bytes long. | |
Returns: Sum An 8-bit unsigned checksum of the array pointed to by pFcbName. | |
*/ | |
int long_name_chksum (int* pFcbName) | |
{ | |
int | |
FcbNameLen, | |
Sum = 0; | |
for(FcbNameLen = 11; FcbNameLen != 0; FcbNameLen -= 1) | |
// The operation is an unsigned char rotate right | |
Sum = ((Sum & 1) ? 0x80 : 0) + (Sum >> 1) + *pFcbName++; | |
return Sum; | |
} | |
/* | |
signed int check_invalid_char(char fname[]) | |
Summary: Checks the filename for any invalid characters. | |
Param: The name of the file to check. | |
Returns: EOF if an invalid character was found, GOODEC otherwise. | |
*/ | |
signed int check_invalid_char(char fname[]) | |
{ | |
int fname_pos; | |
for(fname_pos = 0; (fname[fname_pos] != '\0') && (fname_pos < MAX_FILE_NAME_LENGTH); fname_pos += 1) | |
if(isamoung(fname[fname_pos], "\\/:*?\"<>|")) | |
return EOF; | |
return GOODEC; | |
} | |
/* | |
signed int get_next_free_cluster(int16* my_cluster) | |
Summary: Will go through the FAT and find the first unallocated cluster. | |
Param: Pointer to a variable that will the the starting cluster of the serach. | |
When a free cluster is found, the cluster number will be put into this variable. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
Note: This gets a little slow when dealing with a card with lots of stuff on it; sorry about that. | |
*/ | |
#ifdef FAT32 | |
signed int get_next_free_cluster(int32* my_cluster) | |
#else | |
signed int get_next_free_cluster(int16* my_cluster) | |
#endif | |
{ | |
#ifdef FAST_FAT | |
*my_cluster += 1; | |
return GOODEC; | |
#else // NO FAST_FAT | |
#ifdef FAT32 | |
int val[4]; // buffer to hold values | |
int32 cur_cluster; | |
int32 | |
FAT_addr, // the current address that the algorithm is on | |
j; | |
// first, convert *my_cluster to an addressable location in the FAT | |
FAT_addr = (*my_cluster << 2) + FAT_Start; | |
// the most logical place for the next free cluster would be next to the current cluster | |
for(j = 0; j < FAT_Length; j += 4) | |
{ | |
if(mmcsd_read_data(FAT_addr + j, 4, val) != GOODEC) | |
return EOF; | |
cur_cluster = make32(val[3], val[2], val[1], val[0]); | |
if(cur_cluster == 0) | |
{ | |
// add on the last iteration of j, this is how far into the buffer we were when the algorithm terminated | |
FAT_addr += j; | |
// convert *my_cluster back into a cluster number | |
*my_cluster = (FAT_addr - FAT_Start) >> 2; | |
return GOODEC; | |
} | |
} | |
#else // FAT16 | |
int val[2]; | |
int16 cur_cluster; | |
int32 | |
FAT_addr, // the current address that the algorithm is on | |
j; | |
// first, convert *my_cluster to an addressable location in the FAT | |
FAT_addr = (*my_cluster << 1) + FAT_Start; | |
// the most logical place for the next free cluster would be next to the current cluster | |
for(j = 0; j < FAT_Length; j += 2) | |
{ | |
if(mmcsd_read_data(FAT_addr + j, 2, val) != GOODEC) | |
return EOF; | |
cur_cluster = make16(val[1], val[0]); | |
if(cur_cluster == 0) | |
{ | |
// add on the last iteration of j, this is how far into the buffer we were when the algorithm terminated | |
FAT_addr += j; | |
// convert *my_cluster back into a cluster number | |
*my_cluster = (FAT_addr - FAT_Start) >> 1; | |
return GOODEC; | |
} | |
} | |
#endif // #ifdef FAT32 | |
// if we reach this point, we are out of disk space | |
return EOF; | |
#endif // #ifdef FAST_FAT | |
} | |
/* | |
signed int get_next_file(FILE* stream) | |
Summary: Will point the stream to the next file in the directory. | |
Param: The stream to move. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
Note: This will not set the Buf or Flag parameters. | |
*/ | |
signed int get_next_file(FILE* stream) | |
{ | |
int32 | |
cluster, | |
cur_addr, | |
size; | |
int | |
fileentry, | |
filetype; | |
cur_addr = stream->Entry_Addr; | |
do | |
{ | |
// go forward an entry | |
if(get_next_entry(&cur_addr) == EOF) | |
{ | |
stream->File_Type = None; | |
return EOF; | |
} | |
mmcsd_read_data(cur_addr, 1, &fileentry); | |
mmcsd_read_data(cur_addr + 0x0B, 1, &filetype); | |
if(fileentry == 0) | |
{ | |
stream->File_Type = None; | |
return EOF; | |
} | |
} while((fileentry == 0xE5) || (filetype == 0x0F)); | |
// change the stream's file type | |
if(filetype == 0x10) | |
stream->File_Type = Directory; | |
else | |
stream->File_Type = Data_File; | |
// change the stream's entry address | |
stream->Entry_Addr = cur_addr; | |
// read in the new starting cluster | |
mmcsd_read_data(cur_addr + 0x1A, 2, &cluster); | |
mmcsd_read_data(cur_addr + 0x14, 2, (int16*)&cluster + 1); | |
// change the stream's start adress and cur char to the beginning of the first cluster | |
stream->Start_Addr = stream->Cur_Char = cluster_to_addr(cluster); | |
// read in the new file size | |
mmcsd_read_data(cur_addr + 0x1C, 4, &size); | |
// change the stream's size and bytes until EOF | |
stream->Size = stream->Bytes_Until_EOF = size; | |
return GOODEC; | |
} | |
/* | |
signed int get_next_file(FILE* stream) | |
Summary: Will point the stream to the previous file in the directory. | |
Param: The stream to move. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
Note: This will not set the Buf or Flag parameters. | |
*/ | |
signed int get_prev_file(FILE* stream) | |
{ | |
int32 | |
cluster, | |
cur_addr, | |
size; | |
int | |
fileentry = 0xE5, | |
filetype = 0; | |
cur_addr = stream->Entry_Addr; | |
do | |
{ | |
// go backward an entry | |
if(get_prev_entry(&cur_addr) == EOF) | |
{ | |
stream->File_Type = None; | |
return EOF; | |
} | |
mmcsd_read_data(cur_addr, 1, &fileentry); | |
mmcsd_read_data(cur_addr + 0x0B, 1, &filetype); | |
if(fileentry == 0) | |
{ | |
stream->File_Type = None; | |
return EOF; | |
} | |
} while((fileentry == 0xE5) || (filetype == 0x0F)); | |
// change the stream's file type | |
if(filetype == 0x10) | |
stream->File_Type = Directory; | |
else | |
stream->File_Type = Data_File; | |
// change the stream's entry address | |
stream->Entry_Addr = cur_addr; | |
// read in the new starting cluster | |
mmcsd_read_data(cur_addr + 0x1A, 2, &cluster); | |
mmcsd_read_data(cur_addr + 0x14, 2, (int16*)&cluster + 1); | |
// change the stream's start adress and cur char to the beginning of the first cluster | |
stream->Start_Addr = stream->Cur_Char = addr_to_cluster(cluster); | |
// read in the new file size | |
mmcsd_read_data(cur_addr + 0x1C, 4, &size); | |
// change the stream's size and bytes until EOF | |
stream->Size = stream->Bytes_Until_EOF = size; | |
return GOODEC; | |
} | |
/* | |
signed int get_next_free_addr(int32* my_addr) | |
Summary: Finds the next unallocated address. | |
Param: Pointer to a variable that will the the starting address of the serach. | |
When a free address is found, the address will be put into this variable. | |
Returns: EOF if there was a problem, GOODEC if everything went okay. | |
*/ | |
signed int get_next_free_addr(int32* my_addr) | |
{ | |
int val; // buffer to hold values | |
int32 cur_addr; // pointer to memory | |
// make a copy of *my_addr | |
cur_addr = *my_addr; | |
// keep on getting addresses until we hit a free one | |
do | |
{ | |
if(mmcsd_read_data(cur_addr, 1, &val) != GOODEC) | |
return EOF; | |
if(get_next_addr(&cur_addr) == EOF) | |
return EOF; | |
} while(val != 0); | |
*my_addr = cur_addr; | |
return GOODEC; | |
} | |
/* | |
signed int get_next_entry(int32* start_addr) | |
Summary: Gets the next entry in a directory. | |
Param: The address to start looking for an entry. | |
If an entry is found, it will be put into this variable. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int get_next_entry(int32* start_addr) | |
{ | |
int32 i; | |
i = *start_addr; | |
i += 0x1F; | |
if(get_next_addr(&i) == EOF) | |
return EOF; | |
*start_addr = i; | |
return GOODEC; | |
} | |
/* | |
signed int get_prev_entry(int32* start_addr) | |
Summary: Finds the next free entry in a directory. | |
Param: The address to start looking for a free entry. | |
If a free entry is found, it will be put into this variable. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int get_prev_entry(int32* start_addr) | |
{ | |
int32 i; | |
i = *start_addr; | |
i -= 0x1F; | |
if(get_prev_addr(&i) == EOF) | |
return EOF; | |
*start_addr = i; | |
return GOODEC; | |
} | |
/* | |
signed int get_next_free_entry(int32* start_addr) | |
Summary: Finds the next free entry in a directory. | |
Param: The address to start looking for a free entry. | |
If a free entry is found, it will be put into this variable. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int get_next_free_entry(int32* start_addr) | |
{ | |
int buf; | |
int32 i; | |
i = *start_addr; | |
if(mmcsd_read_data(i, 1, &buf) != GOODEC) | |
return EOF; | |
while(buf != 0) | |
{ | |
i += 0x1F; | |
// get the next address | |
if(get_next_addr(&i) == EOF) | |
if(alloc_clusters(addr_to_cluster(i), &i) == EOF) | |
return EOF; | |
if(mmcsd_read_data(i, 1, &buf) != GOODEC) | |
return EOF; | |
} | |
*start_addr = i; | |
return GOODEC; | |
} | |
/* | |
signed int alloc_clusters(int16 start_cluster, int32* new_cluster_addr) | |
Summary: Find, allocate, and link a free cluster. | |
Param start_cluster: The cluster to begin looking for free clusters. This cluster will be linked to the newfound cluster in the FAT. | |
Param new_cluster_addr: The address of the newly allocated cluster. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
#ifdef FAT32 | |
signed int alloc_clusters(int32 start_cluster, int32* new_cluster_addr) | |
#else | |
signed int alloc_clusters(int16 start_cluster, int32* new_cluster_addr) | |
#endif | |
{ | |
#ifdef FAT32 | |
int32 | |
cur_cluster, | |
next_cluster; | |
#else // FAT16 | |
int16 | |
cur_cluster, | |
next_cluster; | |
#endif // #ifdef FAT32 | |
// if we're at the end of the file's allocated space, then we need to allocate some more space | |
// figure out where the current character is pointing to | |
next_cluster = cur_cluster = start_cluster; | |
// get the next free cluster | |
if(get_next_free_cluster(&next_cluster) == EOF) | |
return EOF; | |
if(write_fat(cur_cluster, next_cluster) == EOF) | |
return EOF; | |
#ifdef FAT32 | |
if(write_fat(next_cluster, 0x0FFFFFFF) == EOF) | |
return EOF; | |
#else // FAT16 | |
if(write_fat(next_cluster, 0xFFFF) == EOF) | |
return EOF; | |
#endif // #ifdef FAT32 | |
// erase all of the data in the newly linked cluster | |
if(clear_cluster(next_cluster) == EOF) | |
return EOF; | |
// put the current character to this position | |
*new_cluster_addr = cluster_to_addr(next_cluster); | |
return GOODEC; | |
} | |
/* | |
signed int dealloc_clusters(int16 start_cluster) | |
Summary: De-allocates linked clusters from the FAT. | |
Param: The starting cluster to deallocate. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
#ifdef FAT32 | |
signed int dealloc_clusters(int32 start_cluster) | |
#else | |
signed int dealloc_clusters(int16 start_cluster) | |
#endif | |
{ | |
#ifdef FAT32 | |
int32 | |
cur_cluster, // the current cluster we're pointing to | |
next_cluster; // the next cluster we're going to point to | |
#else // FAT16 | |
int16 | |
cur_cluster, // the current cluster we're pointing to | |
next_cluster; // the next cluster we're going to point to | |
#endif // #ifdef FAT32 | |
// figure out where the first cluster is | |
next_cluster = cur_cluster = start_cluster; | |
do | |
{ | |
// get the next cluster | |
if(get_next_cluster(&next_cluster) == EOF) | |
return EOF; | |
// unlink the current cluster in the FAT | |
if(write_fat(cur_cluster, 0) == EOF) | |
return EOF; | |
cur_cluster = next_cluster; | |
} | |
#ifdef FAT32 | |
while(cur_cluster != 0x0FFFFFFF); | |
#else // FAT16 | |
while(cur_cluster != 0xFFFF); | |
#endif // #ifdef FAT32 | |
return GOODEC; | |
} | |
/* | |
signed int clear_cluster(int16 cluster) | |
Summary: Clears out all of the data in a given cluster. | |
Param: The cluster to clear out. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
#ifdef FAT32 | |
signed int clear_cluster(int32 cluster) | |
#else | |
signed int clear_cluster(int16 cluster) | |
#endif | |
{ | |
int | |
clear_entry[0x20], | |
j; | |
int16 i; | |
int32 start_addr; | |
start_addr = cluster_to_addr(cluster); | |
for(j = 0; j < 0x20; j += 1) | |
clear_entry[j] = 0; | |
for(i = 0; i < Bytes_Per_Cluster; i += 0x20) | |
if(mmcsd_write_data(start_addr + i, 0x20, clear_entry) != GOODEC) | |
return EOF; | |
return GOODEC; | |
} | |
/* | |
signed int write_fat(int32 cluster, int32 data) | |
Summary: Writes specified data about a cluster to the FAT. | |
Param cluster: The cluster to modify the in the FAT. | |
Param data: The data about the cluster to put into the FAT. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
#ifdef FAT32 | |
signed int write_fat(int32 cluster, int32 data) | |
{ | |
if(mmcsd_write_data((cluster << 2) + FAT_Start, 4, &data) != GOODEC) | |
return EOF; | |
return GOODEC; | |
} | |
#else // FAT16 | |
signed int write_fat(int16 cluster, int16 data) | |
{ | |
if(mmcsd_write_data((cluster << 1) + FAT_Start, 2, &data) != GOODEC) | |
return EOF; | |
return GOODEC; | |
} | |
#endif // #ifdef FAT32 | |
/* | |
signed int read_buffer(FILE* stream, int* val) | |
Summary: Reads from the buffer. | |
Param stream: The stream whose buffer to read from. | |
Param val: A pointer to a varaible to put the read data into. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int read_buffer(FILE* stream, int* val) | |
{ | |
int i; // counter for loops | |
// check to see if we need to populate the buffer | |
if((stream->Cur_Char % STREAM_BUF_SIZE) == 0) | |
{ | |
if(mmcsd_read_data(stream->Cur_Char, STREAM_BUF_SIZE, stream->Buf) != GOODEC) | |
{ | |
stream->Flags |= Read_Error; | |
return EOF; | |
} | |
} | |
// grab the value at the top of the buffer | |
*val = stream->Buf[0]; | |
// shift everything over 1 byte to put a new value at the top of the buffer | |
for(i = 0; i < 8; i += 1) | |
rotate_right(stream->Buf, STREAM_BUF_SIZE); | |
return GOODEC; | |
} | |
/* | |
signed int write_buffer(FILE* stream, int val) | |
Summary: Writes to the buffer. | |
Param stream: The stream whose buffer to write to. | |
Param val: A variable to write to the buffer. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int write_buffer(FILE* stream, int val) | |
{ | |
// check to see if we should dump the buffer to the card | |
if(((stream->Cur_Char % STREAM_BUF_SIZE) == 0) | |
&& (stream->Cur_Char != stream->Start_Addr)) | |
{ | |
// dump the buffer to the card | |
if(mmcsd_write_data(stream->Cur_Char - STREAM_BUF_SIZE, STREAM_BUF_SIZE, stream->Buf) != GOODEC) | |
{ | |
stream->Flags |= Write_Error; | |
return EOF; | |
} | |
} | |
// fill up a byte on the buffer | |
stream->Buf[stream->Cur_Char % STREAM_BUF_SIZE] = val; | |
return GOODEC; | |
} | |
/* | |
void fill_entry(char the_entry[], char val, int8 start_ind) | |
Summary: This will fill up the unused spots in a standard FAT entry with a value. | |
Param the_entry[]: The entry that will be modified. | |
Param val: The value to fill the entry with. | |
Param start_ind: The beginning index to start filling the_entry. | |
Returns: Nothing. | |
*/ | |
void fill_entry(char the_entry[], char val, int8 start_ind) | |
{ | |
int8 i; | |
for(i = start_ind; i < 13; i += 1) | |
{ | |
if(i < 5) | |
{ | |
the_entry[(i << 1) + 1] = val; | |
the_entry[(i << 1) + 2] = val; | |
} | |
else if(i < 11) | |
{ | |
the_entry[(i << 1) + 4] = val; | |
the_entry[(i << 1) + 5] = val; | |
} | |
else | |
{ | |
the_entry[(i << 1) + 6] = val; | |
the_entry[(i << 1) + 7] = val; | |
} | |
} | |
} | |
/* | |
void disp_timestamp(int16 timestamp) | |
Summary: Parses an timestamp from a file entry and displays it to the console | |
Param: The 16-bit timestamp code from a file entry | |
Returns: Nothing. | |
*/ | |
void disp_timestamp(int16 timestamp) | |
{ | |
// Hours:Minutes:Seconds | |
printf("%lu:%lu:%lu", | |
timestamp >> 11, | |
(timestamp & 0x07E0) >> 5, | |
(timestamp & 0x001F) << 1); | |
} | |
/* | |
void disp_datestamp(int16 datestamp) | |
Summary: Parses an datestamp from a file entry and displays it to the console | |
Param: The 16-bit datestamp code from a file entry | |
Returns: Nothing. | |
*/ | |
void disp_datestamp(int16 datestamp) | |
{ | |
// Month/Day/Year | |
printf("%lu/%lu/%lu", | |
(datestamp & 0x01F0) >> 5, | |
datestamp & 0x001F, | |
(1980 + (datestamp >> 9))); | |
} | |
/// Data Utility Functions /// | |
/* | |
signed int fat_init() | |
Summary: Initializes global variables that are essential for this library working | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
Note: This must be called before any other function calls in this library. | |
*/ | |
signed int fat_init() | |
{ | |
int ec = 0; | |
int | |
FATs, | |
Sectors_Per_Cluster; | |
int16 | |
Bytes_Per_Sector, | |
Reserved_Sectors, | |
Small_Sectors; | |
int32 | |
Hidden_Sectors, | |
Large_Sectors; | |
int16 | |
Root_Entries, | |
Sectors_Per_FAT; | |
// initialize the media | |
ec += mmcsd_init(); | |
// start filling up variables | |
ec += mmcsd_read_data(11, 2, &Bytes_Per_Sector); | |
ec += mmcsd_read_data(13, 1, &Sectors_Per_Cluster); | |
ec += mmcsd_read_data(14, 2, &Reserved_Sectors); | |
ec += mmcsd_read_data(16, 1, &FATs); | |
ec += mmcsd_read_data(17, 2, &Root_Entries); | |
ec += mmcsd_read_data(19, 2, &Small_Sectors); | |
ec += mmcsd_read_data(22, 2, &Sectors_Per_FAT); | |
ec += mmcsd_read_data(28, 4, &Hidden_Sectors); | |
ec += mmcsd_read_data(32, 4, &Large_Sectors); | |
Next_Free_Clust = 2; | |
if(ec != GOODEC) | |
return EOF; | |
// figure out the size of a cluster | |
Bytes_Per_Cluster = Sectors_Per_Cluster * Bytes_Per_Sector; | |
// figure out how long one FAT is | |
FAT_Length = Sectors_Per_FAT * (int32)Bytes_Per_Sector; | |
// figure out where the FAT starts | |
FAT_Start = Reserved_Sectors * Bytes_Per_Sector; | |
// figure out where the root directory starts | |
Root_Dir = FAT_Start + (FATs * FAT_Length); | |
// figure out where data for files in the root directory starts | |
Data_Start = (Root_Entries * 0x20) + (Bytes_Per_Sector - 1); | |
Data_Start /= Bytes_Per_Sector; | |
Data_Start += Reserved_Sectors + (FATs * Sectors_Per_FAT); | |
Data_Start *= Bytes_Per_Sector; | |
return GOODEC; | |
} | |
/* | |
signed int get_next_cluster(int16* my_cluster) | |
Summary: Gets the next linked cluster from the FAT. | |
Param: A pointer to a variable that holds a cluster. | |
This variable will then have the next linked cluster when the function returns. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int get_next_cluster(int16* my_cluster) | |
{ | |
// convert the current cluster into the address of where information about | |
// the cluster is stored in the FAT, and put this value into the current cluster | |
if(mmcsd_read_data((*my_cluster << 1) + FAT_Start, 2, my_cluster) != GOODEC) | |
return EOF; | |
return GOODEC; | |
} | |
/* | |
signed int get_prev_cluster(int32* my_cluster) | |
Summary: Gets the previously linked cluster in the FAT. | |
Param: A pointer to a variable that holds a cluster. | |
This variable will then have the previous linked cluster when the function returns. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
#ifdef FAT32 | |
signed int get_prev_cluster(int32* my_cluster) | |
#else | |
signed int get_prev_cluster(int16* my_cluster) | |
#endif // #ifdef FAT32 | |
{ | |
#ifdef FAT32 | |
int32 | |
cur_cluster = 1, | |
target_cluster = 0; | |
#else | |
int16 | |
cur_cluster = 1, | |
target_cluster = 0; | |
#endif // #ifdef FAT32 | |
while(target_cluster != *my_cluster) | |
{ | |
cur_cluster += 1; | |
#ifdef FAT32 | |
if(mmcsd_read_data((cur_cluster << 2) + FAT_Start, 4, &target_cluster) != GOODEC) | |
return EOF; | |
#else // FAT16 | |
if(mmcsd_read_data((cur_cluster << 1) + FAT_Start, 2, &target_cluster) != GOODEC) | |
return EOF; | |
#endif // #ifdef FAT32 | |
} | |
#ifdef FAT32 | |
*my_cluster = cur_cluster; | |
#else // FAT16 | |
*my_cluster = cur_cluster; | |
#endif // #ifdef FAT32 | |
return GOODEC; | |
} | |
/* | |
signed int get_next_addr(int32* my_addr) | |
Summary: Get the next linked address. | |
Param: A pointer to a variable that holds an address. | |
This variable will then have the next linked address when the function returns. | |
Returns: EOF if there was a problem with the media or we've reached the last linked cluster in the FAT, GOODEC if everything went okay. | |
*/ | |
signed int8 get_next_addr(int32* my_addr) | |
{ | |
int32 temp; | |
#ifdef FAT32 | |
int32 c; | |
#else | |
int16 c; | |
#endif | |
// check to make sure that the next iteration will give us a contiguous address | |
temp = *my_addr + 1; | |
if((temp > Data_Start) && ((temp - Data_Start) % Bytes_Per_Cluster == 0)) | |
{ | |
// convert the current address into the address of where information about | |
// the address is stored in the FAT, and put this value into the current address | |
c = addr_to_cluster(temp - 1); | |
if(get_next_cluster(&c) == EOF) | |
return EOF; | |
if (c >= | |
#ifdef FAT32 | |
0x0FFFFFF8 | |
#else | |
0xFFF8 | |
#endif | |
) | |
return EOF; | |
temp = cluster_to_addr(c); | |
} | |
*my_addr = temp; | |
return GOODEC; | |
} | |
/* | |
signed int get_prev_addr(int32* my_addr) | |
Summary: Get the next linked address. | |
Param: A pointer to a variable that holds an address. | |
This variable will then have the next linked address when the function returns. | |
Returns: EOF if there was a problem with the media or we've reached the last linked cluster in the FAT, GOODEC if everything went okay. | |
*/ | |
signed int8 get_prev_addr(int32* my_addr) | |
{ | |
int32 temp; | |
#ifdef FAT32 | |
int32 c; | |
#else | |
int16 c; | |
#endif | |
temp = *my_addr; | |
// if we're trying to go backwards one entry from the beginning of the root, | |
// we won't be able to... | |
if(temp <= Root_Dir) | |
return GOODEC; | |
// check to make sure that the next iteration will give us a contiguous address | |
if((temp >= Data_Start) && ((temp - Data_Start) % Bytes_Per_Cluster == 0)) | |
{ | |
c = addr_to_cluster(temp); | |
if(get_prev_cluster(&c) == EOF) | |
return EOF; | |
temp = cluster_to_addr(c) + Bytes_Per_Cluster; | |
} | |
*my_addr = temp - 1; | |
return GOODEC; | |
} | |
/* | |
int32 cluster_to_addr(int32 cluster) | |
Summary: Converts a cluster number to an address. | |
Param: The cluster to convert. | |
Returns: The cluster's address. | |
*/ | |
#ifdef FAT32 | |
int32 cluster_to_addr(int32 cluster) | |
{ | |
// in unit math: | |
// return Bytes+(Bytes / Cluster * (Clusters - Clusters)) | |
return Root_Dir + (Bytes_Per_Cluster * (cluster - 2)); | |
} | |
#else | |
int32 cluster_to_addr(int16 cluster) | |
{ | |
if(cluster < 2) | |
return 0; | |
// in unit math: | |
// return Bytes + ( Bytes / Cluster * (Clusters- Clusters)) | |
return Data_Start + ((int32)Bytes_Per_Cluster * (cluster - 2)); | |
} | |
#endif | |
/* | |
int32 addr_to_cluster(int32 addr) | |
Summary: Converts an address to a cluster number. | |
Param: The address to convert. | |
Returns: The address's cluster. | |
*/ | |
#ifdef FAT32 | |
int32 addr_to_cluster(int32 addr) | |
{ | |
// in unit math: | |
// return (Bytes -Bytes) / Bytes / Cluster) + Clusters | |
return ((addr - Root_Dir) / Bytes_Per_Cluster) + 2; | |
} | |
#else | |
int16 addr_to_cluster(int32 addr) | |
{ | |
if(addr < Data_Start) | |
return 0; | |
// in unit math: | |
// return (Bytes -Bytes) /(Bytes / Cluster) + Clusters | |
return ((addr - Data_Start) / Bytes_Per_Cluster) + 2; | |
} | |
#endif | |
/// Debugging Utility Functions /// | |
/* | |
signed int disp_folder_contents(char foldername[]) | |
Summary: Displays the contents of a folder. | |
Param: The folder to display the contents of. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int disp_folder_contents(char foldername[]) | |
{ | |
char filename[MAX_FILE_NAME_LENGTH]; // a place to hold a file name | |
FILE stream; // the stream that we're going to be working with | |
char mode[] = "r"; | |
if(fatopen(foldername, mode, &stream) != GOODEC) | |
return EOF; | |
// printf off a header | |
printf("\r\n--%s--", foldername); | |
// start off at the root directory | |
stream.Entry_Addr = stream.Start_Addr; | |
while(get_next_file(&stream) != EOF) | |
{ | |
// get the name of the file that we are at | |
if(get_file_name(stream.Entry_Addr, filename) != GOODEC) | |
return EOF; | |
// make cool little "tree" branches | |
printf("\r\n%s", filename); | |
if (stream.File_Type == Directory) | |
putc('/'); | |
} | |
fatclose(&stream); | |
return GOODEC; | |
} | |
/* | |
signed int dump_addr(int32 from, int32 to) | |
Summary: Display a series of addresses in a hex editor type fashion. | |
Param from: The beginning address to display. | |
Param to: The end address to display. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int dump_addr(int32 from, int32 to) | |
{ | |
int | |
j, // counter for loops | |
val[0x10]; // buffer to hold values | |
int32 i; // pointer to memory | |
// print off header | |
printf("\r\n\r\n "); | |
for(i = 0; i < 0x10; i += 1) | |
printf("%2X ", i); | |
// note that the to and from values are being rounded up and down | |
// this makes a nice "block" map in case someone inputs a number that | |
// isn't evenly divisible by 0x10 | |
for(i = (from - (from % 0x10)); i <= (to + (to % 0x10)); i += 0x10) | |
{ | |
// printf memory block | |
printf("\r\n%lX ", i); | |
// fill up buffer | |
if(mmcsd_read_data(i, 0x10, val) != GOODEC) | |
return EOF; | |
// printf RAM in hex | |
for(j = 0; j < 0X10; j += 1) | |
printf("%X ", val[j]); | |
// printf spacer | |
printf("; "); | |
// printf RAM in char | |
for(j = 0; j < 0X10; j += 1) | |
{ | |
// check for characters that will mess up the nice-looking map | |
if(val[j] < ' ') | |
val[j] = '.'; | |
printf("%c", val[j]); | |
} | |
} | |
return GOODEC; | |
} | |
/* | |
signed int dump_clusters(int32 from, int32 to) | |
Summary: Display a series of clusters in a memory map. | |
Param from: The beginning clusters to display. | |
Param to: The end clusters to display. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int dump_clusters(int32 from, int32 to) | |
{ | |
// convert the clusters to addresses and dump | |
if(dump_addr(cluster_to_addr(from), cluster_to_addr(to)) != GOODEC) | |
return EOF; | |
} | |
/* | |
void disp_fat_stats() | |
Summary: Display essential statistics about the FAT to the console. | |
Returns: Nothing. | |
*/ | |
void disp_fat_stats() | |
{ | |
printf("\r\n\r\n--FAT Stats--\r\n"); | |
printf("First FAT starts at: 0x%lX\r\n", FAT_Start); | |
printf("Data Starts At: 0x%lX\r\n", Data_Start); | |
printf("Root Directory Is At: 0x%lX\r\n", Root_Dir); | |
printf("Bytes Per Cluster: 0x%lX\r\n", Bytes_Per_Cluster); | |
} | |
/* | |
signed int fatprintfinfo(FILE* stream) | |
Summary: Display essential statistics about the file that a stream is pointing to. | |
Param: The stream to print off information about. | |
Returns: EOF if there was a problem with the media, GOODEC if everything went okay. | |
*/ | |
signed int fatprintfinfo(FILE* stream) | |
{ | |
int ec = 0; | |
int32 val = 0; // buffer to hold values | |
char name[MAX_FILE_NAME_LENGTH]; | |
// get name | |
if(get_file_name(stream->Entry_Addr, name) != GOODEC) | |
return EOF; | |
// printf header | |
printf("\r\n\r\n--"); | |
printf(name); | |
printf(" Info--"); | |
// printf attributes | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x0B, 1, &val); | |
printf("\r\nAttributes: 0x%X", val); | |
// printf creation date | |
printf("\r\nCreated: "); | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x10, 2, &val); | |
disp_datestamp(val); | |
printf(" "); | |
// printf creation time | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x0E, 2, &val); | |
disp_timestamp(val); | |
// printf modification date | |
printf("\r\nModified: "); | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x18, 2, &val); | |
disp_datestamp(val); | |
printf(" "); | |
// printf modification time | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x16, 2, &val); | |
disp_timestamp(val); | |
// printf starting cluster | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x14, 2, (int16*)&val + 1); | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x1A, 2, &val); | |
printf("\r\nStarting cluster: %lX", val); | |
// printf starting address | |
printf("\r\nStarting address: %lX", cluster_to_addr(val)); | |
// printf size | |
ec += mmcsd_read_data(stream->Entry_Addr + 0x1C, 4, &val); | |
printf("\r\nSize: %lu Bytes\r\n", val); | |
if(ec != GOODEC) | |
return EOF; | |
return GOODEC; | |
} | |
#endif // #ifndef FAT_PIC_C | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment