BASIC

Author Topic: File Handling Custom Type  (Read 1408 times)

Offline Slydog

  • Prof. Inline
  • *****
  • Posts: 930
  • KodeSource
    • View Profile
    • KodeSource
File Handling Custom Type
« on: 2011-Jul-07 »
I needed a break from my game, so I thought I'd contribute something.

Programming with so many languages, I hate having to learn each language's methods of doing things, such as file handling.
What I like to do is wrap those commands in a common wrapper, so my code appears similar across the different languages.

Here's my attempt at simplifying the file handling commands of GLB.
It only covers the basic types of file commands people need.

Code: GLBasic [Select]
CONSTANT FILE_MODE_APPEND = -1
CONSTANT FILE_MODE_WRITE = 0
CONSTANT FILE_MODE_READ = 1

CONSTANT EOF$ = "<EOF>"

TYPE TFile
        fh%                                             // File Handle
        name$                                   // File Name
        data$                                   // File Contents
        eof%                                    // File is at end?

        // Reads entire file into 'self.data$'.  Closes file when finished.
        // 'name$' > Optional.  Will use previously set 'self.name$' if blank.
        // Returns > [TRUE if succeed | FALSE if fail]
        FUNCTION Get%: name$=""
                IF name$ <> "" THEN self.name$ = name$
                self.data$ = ""
                LOCAL line$ = ""
                IF self.Open(FILE_MODE_READ)
                        WHILE ENDOFFILE(self.fh) = FALSE
                                READLINE self.fh, line$
                                INC self.data$, line$
                        WEND
                        CLOSEFILE self.fh
                ELSE
                        LOG(">FIL:{TFile.Get$      } *** Error Getting File: [" + self.name$ + "] ***")
                        RETURN FALSE
                ENDIF
                self.Close()
                RETURN TRUE
        ENDFUNCTION

        // Reads the next line in an already opened file.  Closes file when end is reached.
        // Returns > [line data if succeed | 'EOF$' if fail]
        FUNCTION GetLine$:
                LOCAL line$
                IF ENDOFFILE(self.fh)
                        line$ = EOF$
                        self.eof = TRUE
                        self.Close()
                ELSE
                        self.eof = FALSE
                        READLINE self.fh, line$
                ENDIF
                RETURN line$
        ENDFUNCTION

        // Writes 'self.data$' to file
        // 'name$' > Optional.  Will use previously set 'self.name$' if blank.
        // Returns > [TRUE if succeed | FALSE if fail]
        FUNCTION Write%: name$=""
                IF name$ <> "" THEN self.name$ = name$
                IF self.Open(FILE_MODE_WRITE)
                        WRITESTR self.fh, self.data$
                ELSE
                        LOG(">FIL:{TFile.Write$    } *** Error Writting File: [" + self.name$ + "] ***")
                        RETURN FALSE
                ENDIF
                self.Close()
                RETURN TRUE
        ENDFUNCTION

        // Appends string to end of file.
        // 'extra$'> Data to append
        // 'name$' > Optional.  Will use previously set 'self.name$' if blank.
        // Returns > [TRUE if succeed | FALSE if fail]
        FUNCTION Append%: extra$, name$=""
                IF name$ <> "" THEN self.name$ = name$
                IF self.Open(FILE_MODE_APPEND)
                        INC self.data$, extra$
                        WRITESTR self.fh, extra$
                ELSE
                        LOG(">FIL:{TFile.Append$   } *** Error Appending File: [" + extra$ + "] ***")
                        RETURN FALSE
                ENDIF
                self.Close()
                RETURN TRUE
        ENDFUNCTION

        // Opens file for procesing.
        // 'mode%' > [FILE_MODE_APPEND | FILE_MODE_WRITE | FILE_MODE_READ]
        // 'name$' > Optional.  Will use previously set 'self.name$' if blank.
        // Returns > [TRUE if succeed | FALSE if fail]
        FUNCTION Open%: mode%, name$=""
                IF name$ <> "" THEN self.name$ = name$
                self.fh = GENFILE()
                self.eof = FALSE
                IF OPENFILE(self.fh, self.name$, mode) = FALSE
                        LOG(">FIL:{TFile.Open      } *** Error Opening File: [" + self.name$ + "] ***")
                        self.eof = TRUE
                        RETURN FALSE
                ENDIF
                RETURN TRUE
        ENDFUNCTION

        // Closes file to further processing
        FUNCTION Close%:
                IF self.fh >= 0 THEN CLOSEFILE self.fh
                self.fh = -1
                self.eof = TRUE
        ENDFUNCTION

        // Checks if file exists
        // Returns > [TRUE if exists | FALSE if doesn't]
        FUNCTION Exists%:
                RETURN DOESFILEEXIST(self.name$)
        ENDFUNCTION

        // Returns file name portion from a longer file path
        // eg. For a 'self.name$' of [C:/Programs/GLBasic/glbasic.exe] will return [glbasic.exe]
        // 'slash$' > Optional.  Folder Seperator.  Use if your path uses '\' instead. Uses '/' as default
        FUNCTION GetName$: slash$="/"
                LOCAL slash%
                slash = REVINSTR(self.name$, slash$)
                IF slash <=0 THEN RETURN self.name$
                RETURN MID$(self.name$, slash+1)
        ENDFUNCTION

ENDTYPE


FUNCTION LOG: message$, add_linefeed% = TRUE, show_time% = TRUE
        STATIC datetime% = -1                                                                   // Log timer
        LOCAL elapsed#                                                                                  // How much time has elapsed since program start (or manual reset)
        IF datetime < 0 THEN datetime = GETTIMERALL()                   // Initialize log timer if not already set
        IF message$ = "RESET" THEN datetime = GETTIMERALL()             // Manually reset log timer
        elapsed = (GETTIMERALL() - datetime) / 1000.0
        ?IFDEF WIN32
                //?IFDEF GLB_DEBUG
                //?ENDIF
                IF show_time = TRUE THEN DEBUG "[" + FORMAT$(8, 3, elapsed) + "] "
                DEBUG message$
                IF add_linefeed THEN DEBUG "\n"
        ?ELSE
                INLINE
                        STDOUT(message_Str);
                ENDINLINE
        ?ENDIF
ENDFUNCTION

Here's some basic usages examples:

Code: GLBasic [Select]
LOCAL file AS TFile
LOCAL line$

// Read settings
IF NOT file.Get("settings.ini") THEN RETURN FALSE
DEBUG "Data:" + file.data$

// Write new settings data
file.data$ = "abcdefghijklmnopqrstuvwxyz"
file.Write("settings.ini")
file.Append("ZYXWVUTSR", "settings.ini"

// Optional method by setting file name directly
file.name$ = "scores.txt"
IF NOT file.Exists() THEN RETURN FALSE
file.data$ = "slydog | 12345"
file.Write()
file.Append("glbasic | 12346")

// Parsing a file line by line
IF NOT file.Open(FILE_MODE_READ, "graphics/fonts/comics.dat") THEN RETURN FALSE  // Exit and return 'FALSE' if can't open

REPEAT
    line$ = file.GetLine$()
    IF file.eof THEN GOTO SKIP                                                                                  // End of file  -> safe usage of 'GOTO' IMO :)
    IF LEN(line$) <= 0 THEN GOTO SKIP                                                                   // Skip blank lines
    //  continue with parsing . . .
    SKIP:
UNTIL file.eof
file.Close()

The sample code wasn't tested, so no guarantees! 
And other commands weren't tested much either, so let me know if I have any bugs.
(or calls to my other functions I didn't include!)

I included my 'LOG()' command too as I find it useful for logging errors or for debugging code.
Also, it shows the time in ms since the program started so could be useful for identifying bottlenecks.
Here's a typical Output window for my game using this command:
Code: GLBasic [Select]
[   0.001] >GFX:{Sprite_Load     } fn:[Graphics/skin.png] | id:[1]
[   0.117] >GFX:{Sprite_Load     } fn:[Graphics/preview.png] | id:[3]
[   0.120] >GFX:{Sprite_Load     } fn:[Graphics/Fonts/blambot_secret_origins_32x50.png] | id:[4]
[   0.182] >GFX:{Sprite_Load     } *** File NOT FOUND!: [Graphics/Fonts/Kartika.png]
[   0.183] >FIL:{TFile.Open      } *** Error Opening File: [Graphics/Fonts/Kartika.dat] ***
[   0.184] >FON:{TGlyph.New%     } *** Error Opening Glyph File: [Graphics/Fonts/Kartika.dat] ***
[   0.186] >GFX:{Sprite_Load     } fn:[Graphics/Skins/Skin_Basic.png] | id:[5]
[   0.207] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[240,200,  0] | uv:[187,178]-[227,218]
[   0.234] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[  0,  0,255] | uv:[  0,192]-[ 48,240]
[   0.235] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[  0,  0,255] | uv:[ 48,192]-[ 96,240]
[   0.237] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[  0,  0,255] | uv:[ 96,192]-[144,240]
[   0.238] >GFX:{TSpriteUv       } sprite:[ 5] | rgb:[  0,  0,255] | uv:[144,192]-[186,240]
[   0.243] >GAM:{State           } STATE_MENU_MAIN
[   3.643] >GAM:{State           } STATE_MENU_WORLD
[   6.510] >GUI:{Scroll.IsScroll } FLICK Touch . . .
[   6.626] >GUI:{Scroll.IsScroll } FLICK Canceled: Finger Off
[   6.631] >GUI:{MenuWorld Select} id:[1000] | name:[Easy Peasy]
[   6.634] >GAM:{State           } STATE_MENU_LEVEL
[   6.728] >MAZ:{Maze_Generate   } >>>
[   6.742] >MAZ:{Maze_Generate   } <<< Done!
[   6.771] >GFX:{ShaderLoad      } fn:[/TwistedMaze/Window.app/Media/Graphics/Shaders/Maze]
[   6.887] >GFX:{ShaderLoad      } fn:[/TwistedMaze/Window.app/Media/Graphics/Shaders/Color]
[   8.770] >MAZ:{Maze_Generate   } >>>
[   8.777] >MAZ:{Maze_Generate   } <<< Done!
[   8.782] >GAM:{State           } STATE_PLAYING_MAZE
[   8.897] >GAM:{HudDraw         } stars:1
[  10.714] >GAM:{GLB_ON_QUIT     } Exiting >>>
 
« Last Edit: 2011-Jul-07 by Slydog »
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]