I was unsatisfied with KermM's C port of AHelper's C++ file browser library. It caused a lot of system errors on my under-development image viewer, and its code "complexity" annoyed me as a beginner in C coding.
So I wrote my own file browser, first for specific purposes and now modified to fit most usage cases. It doesn't use dynamic memory allocation and supports a maximum of 200 files or folders per directory (same as OS).
Long file names are supported (that and the fact that it doesn't show the filesize are probably the reasons why it doesn't mimic the OS so well).
I'm yet to get a single system error with it.
I believe the code is pretty self-explanatory and easy to change according to your needs. There's an usage example at the bottom (and after removing it, the code should compile as-is).
When compiling, one or two warnings are returned but I don't know how to fix them.
Tell me if something doesn't work.
Code:
So I wrote my own file browser, first for specific purposes and now modified to fit most usage cases. It doesn't use dynamic memory allocation and supports a maximum of 200 files or folders per directory (same as OS).
Long file names are supported (that and the fact that it doesn't show the filesize are probably the reasons why it doesn't mimic the OS so well).
I'm yet to get a single system error with it.
I believe the code is pretty self-explanatory and easy to change according to your needs. There's an usage example at the bottom (and after removing it, the code should compile as-is).
When compiling, one or two warnings are returned but I don't know how to fix them.
Tell me if something doesn't work.
Code:
/* FILE BROWSER "LIBRARY" for the Casio Prizm series of calculators
Version 1.0 - Dec. 20, 2012
For use along with libfxcg.
Written by: gbl08ma <gbl08ma@gmail.com> - http://gbl08ma.com
at tny. internet media - http://i.tny.im
Released as public domain code
(but giving credit where possible won't hurt anyone, even though
it isn't a must).
This code is designed to mimic as much as possible the default
OS file selecting screens like those seen in Picture Plot and
eActivity.
*/
void CopySpriteMasked(const unsigned char* data, int x, int y, int width, int height, int maskcolor) {
char* VRAM = (char*)0xA8000000;
VRAM += 2*(LCD_WIDTH_PX*y + x);
for(int j=y; j<y+height; j++) {
for(int i=x; i<x+width; i++) {
if ((((((int)(*data))&0x000000FF)<<8) | ((((int)(*(data+1))))&0x000000FF)) != maskcolor) {
*(VRAM++) = *(data++);
*(VRAM++) = *(data++);
} else { VRAM += 2; data += 2; }
}
VRAM += 2*(LCD_WIDTH_PX-width);
}
}
const color_t folder_icon[510] = {
0xf81f,0xf81f,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,
0xf81f,0x52aa,0xdef9,0xfffc,0xfffc,0xffba,0xde74,0x52aa,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,0xf81f,
0x52aa,0xdef9,0xfffc,0xffbc,0xffba,0xffb9,0xff78,0xde74,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0xf81f,
0x52aa,0xfffc,0xffbb,0xffba,0xffb9,0xff78,0xff76,0xff34,0xff34,0xfef3,0xf6f1,0xf6f1,0xf6f1,0xf6f1,0xf6f1,0x52aa,0xf81f,
0x52aa,0xffbb,0xffbb,0xffb9,0xff78,0xff76,0xff74,0xff34,0xfef3,0xf6f1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6b1,0x52aa,0xad55,
0x52aa,0xffbb,0xffb9,0xff77,0xff76,0xff35,0xff34,0xfef2,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6b1,0xf6f1,0xf6f1,0x52aa,0xad55,
0x52aa,0xffb9,0xff78,0xff76,0xff35,0xff34,0xfef2,0xf6f2,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0xf6f1,0xf6f1,0xf6b1,0x52aa,0xad55,
0x52aa,0xff77,0xff76,0xff35,0xff33,0xff33,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xeef1,0xf6b1,0xf6b1,0xf6f1,0xf6b1,0x52aa,0xad55,
0x52aa,0xff76,0xff75,0xff34,0xfef3,0xf6f1,0xf6b1,0xf6f1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0x52aa,0xad55,
0x52aa,0xff35,0xff33,0xff32,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xeeb1,0xf6f1,0xf6b1,0xf6f1,0xeeb1,0xf6f1,0xf6b1,0x52aa,0xad55,
0x52aa,0xff33,0xff33,0xf6b2,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0x52aa,0xad55,
0x52aa,0xfef3,0xf6f1,0xf6b1,0xf6b1,0xf6f1,0xeeb1,0xf6f1,0xf6b1,0xf6f1,0xeeb1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0x52aa,0xad55,
0x52aa,0xf6f1,0xf6b1,0xf6f1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0xf6b1,0xf6f1,0xeeb1,0xf6f1,0x52aa,0xad55,
0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0x52aa,0xad55,
0xf81f,0xf81f,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55,0xad55
};
typedef struct
{
char filename[256]; //filename, not proper for use with Bfile.
char name[120]; //friendly name (without //fls0/ or complete path)
int isfolder;
} File;
typedef struct
{
unsigned short id, type;
unsigned long fsize, dsize;
unsigned int property;
unsigned long address;
} file_type_t;
int GetFiles(File files[], char* basepath, unsigned char* filter) {
/*searches storage memory for folders and filtered files, returns their count*/
/*basepath should start with \\fls0\ and should always have a slash (\) at the end */
unsigned short path[0x10A], found[0x10A];
unsigned char buffer[0x10A];
// make the buffer
strcpy((char*)buffer, basepath);
strcat((char*)buffer, "*");
int curitem = 0;
file_type_t fileinfo;
int findhandle;
Bfile_StrToName_ncpy(path, buffer, 0x10A);
int ret = Bfile_FindFirst_NON_SMEM((const char*)path, &findhandle, (char*)found, &fileinfo);
Bfile_StrToName_ncpy(path, filter, 0x10A);
while(!ret) {
Bfile_NameToStr_ncpy(buffer, found, 0x10A);
if(!(strcmp((char*)buffer, "..") == 0 || strcmp((char*)buffer, ".") == 0 || strcmp((char*)buffer, "@MainMem") == 0) &&
(fileinfo.fsize == 0 || Bfile_Name_MatchMask((const short int*)path, (const short int*)found)))
{
strcpy(files[curitem].name, (char*)buffer);
strcpy(files[curitem].filename, basepath);
strcat(files[curitem].filename, (char*)buffer);
if(fileinfo.fsize == 0) files[curitem].isfolder = 1; else files[curitem].isfolder = 0;
curitem++;
}
ret = Bfile_FindNext_NON_SMEM(findhandle, (char*)found, (char*)&fileinfo);
}
Bfile_FindClose(findhandle);
return curitem;
}
char browserbasepath[256] = "\\\\fls0\\"; //this has to be out of fileBrowser, if you want to return to the last browsed folder when calling fileBrowser again.
void fileBrowser(char* filename, unsigned char* filter, char* title) {
int inloop = 1;
//<--- put browserbasepath in this line if you don't want it to persist between fileBrowser calls
int curSelFile = 1;
while(inloop) {
int key, inscreen = 1, scroll=0, numfiles=0;
curSelFile = 1;
File files[200];
Bdisp_AllClr_VRAM();
DisplayStatusArea();
numfiles = GetFiles(files, browserbasepath, filter);
while(inscreen) {
Bdisp_AllClr_VRAM();
int curfile = 0; //current processing file
if (numfiles>0) {
while(curfile < numfiles) {
unsigned char menuitem[100] = "";
strcpy((char*)menuitem, " ");
strcat((char*)menuitem, " "); //add space for an icon
strcat((char*)menuitem, (char*)files[curfile].name);
strcat((char*)menuitem, " "); //make sure we have a string big enough to have background when item is selected
if(scroll < curfile+1) {
PrintXY(1,curfile+2-scroll,(char*)menuitem, (curSelFile == curfile+1 ? TEXT_MODE_INVERT : TEXT_MODE_TRANSPARENT_BACKGROUND), TEXT_COLOR_BLACK);
if (files[curfile].isfolder == 1) {
if((curfile+2-scroll)>=2&&(curfile+2-scroll)<=7) CopySpriteMasked((unsigned char*)folder_icon, 18, (curfile+2-scroll)*24+4, 17, 15, 0xf81f );
}
}
curfile++;
}
//hide 8th item
PrintXY(1,8,(char*)" ", TEXT_MODE_NORMAL, TEXT_COLOR_BLACK);
TScrollbar sb;
sb.I1 = 0;
sb.I5 = 0;
sb.indicatormaximum = numfiles;
sb.indicatorheight = 6;
sb.indicatorpos = scroll;
sb.barheight = LCD_HEIGHT_PX - 24*3;
sb.bartop = 24;
sb.barleft = LCD_WIDTH_PX - 6;
sb.barwidth = 6;
Scrollbar(&sb);
} else {
PrintXY(8,4,(char*)" No Data", TEXT_MODE_TRANSPARENT_BACKGROUND, TEXT_COLOR_BLACK);
}
DisplayStatusArea();
char titleBuffer[23] = "";
strcpy(titleBuffer, " ");
strcat(titleBuffer, title);
PrintXY(1, 1, titleBuffer, TEXT_MODE_TRANSPARENT_BACKGROUND, TEXT_COLOR_BLUE);
int textX=strlen(title)*18+10, textY=6;
char friendlypath[256] = "";
strcpy(friendlypath, browserbasepath+6);
friendlypath[strlen(friendlypath)-1] = '\0'; //remove ending slash like OS does
PrintMini(&textX, &textY, (unsigned char*)friendlypath, 0, 0xFFFFFFFF, 0, 0, COLOR_BLACK, COLOR_WHITE, 1, 0);
int iresult;
if(numfiles>0) {
GetFKeyPtr(0x03B1, &iresult); // OPEN
FKey_Display(0, (int*)iresult);
}
GetKey(&key);
switch(key)
{
case KEY_CTRL_DOWN:
if(curSelFile == numfiles)
{
curSelFile = 1;
scroll = 0;
}
else
{
curSelFile++;
if(curSelFile > scroll+(numfiles>6 ? 6 : numfiles))
scroll = curSelFile -(numfiles>6 ? 6 : numfiles);
}
break;
case KEY_CTRL_UP:
if(curSelFile == 1)
{
curSelFile = numfiles;
scroll = curSelFile-(numfiles>6 ? 6 : numfiles);
}
else
{
curSelFile--;
if(curSelFile-1 < scroll)
scroll = curSelFile -1;
}
break;
case KEY_CTRL_EXE:
case KEY_CTRL_F1:
if(files[curSelFile-1].isfolder) {
strcpy(browserbasepath, files[curSelFile-1].filename); //switch to selected folder
strcat(browserbasepath, "\\");
inscreen = 0; //reload at new folder
} else {
strcpy(filename, files[curSelFile-1].filename);
return;
}
break;
case KEY_CTRL_EXIT:
if(!strcmp(browserbasepath,"\\\\fls0\\")) { //check that we aren't already in the root folder
//we are, return
return;
} else {
int i=strlen(browserbasepath)-2;
while (i>=0 && browserbasepath[i] != '\\')
i--;
if (browserbasepath[i] == '\\') {
char tmp[256] = "";
memcpy(tmp,browserbasepath,i+1);
tmp[i+1] = '\0';
strcpy(browserbasepath, tmp);
}
inscreen = 0; //reload at new folder
}
break;
}
}
}
}
/* USAGE EXAMPLE: */
char filename[256] = "";
fileBrowser(filename, (unsigned char*)"*.txt", "Title"); //*.txt: filetype to filter; "Title": small text to show in blue on the top left corner
if(!strcmp(filename, "")) {
//user didn't select a file (pressed EXIT at root folder)
} else {
//user selected a file
//do something with filename (which isn't proper for use with Bfile functions, you need to use Bfile_StrToName_ncpy first!
}