BASIC

Author Topic: a Tvar String Database system (lack a real name)...  (Read 2159 times)

Offline spacefractal

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 3669
    • View Profile
    • Space Fractal
Here is a little code snippit which some might have a nice use for using strings in a different way. I have used this kind of code in years, also in BlitzMax too. I did this because I have never liked number based images system, where I better prefer strings and names. They are easier to remember, but of course its somewhere slowere. But should not been that much, if you use more than one Base names.

I of course use in my next project (last one was a music project for Jungool), which is still secret, due its all using placeholder graphics and is so early. Thís code is nowhere secret.

I known the comments could been better, but hope its self explained.

Code: GLBasic [Select]
// --------------------------------- //
// Project: tVar Database by Space Fractal

TYPE TVar
        Name$=""
        Value$=""
ENDTYPE

TYPE TVarFile
        File$
        VAR[] AS TVar
ENDTYPE

GLOBAL MyVar[] AS TVarFile

// find a string
FUNCTION FindStr$: Search$, Base$
        FOREACH item IN MyVar[]
                IF item.File$=Base$
                        FOREACH item2 IN item.VAR[]
                                LOCAL Se$=LEFT$(item2.Name$, LEN(item2.Name$, Search$))
                                IF Search$=Se$ OR Search$="all"
                                        RETURN item2.Name$
                                ENDIF
                        NEXT
                ENDIF
        NEXT
        RETURN ""
ENDFUNCTION

// get a string
FUNCTION GetStr$: Name$, Base$="def"
        IF Base$="" THEN RETURN ""
        FOREACH item IN MyVar[]
                IF item.File$=Base$
                        FOREACH item2 IN item.VAR[]
                                IF item2.Name$=Name$ THEN RETURN item2.Value$
                        NEXT
                        RETURN ""
                ENDIF
        NEXT
ENDFUNCTION

// remove a string, or use "all" on name, if you want to delete a String Base
FUNCTION RemoveStr: Name$, Base$="def"
        LOCAL COUNT=0
        IF Base$="" THEN RETURN ""
        FOREACH item IN MyVar[]
                IF item.File$=Base$
                        FOREACH item2 IN item.VAR[]
                                IF item2.Name$=Name$ OR Name$="all"
                                        DELETE item2
                                        IF Name$<>"ALL" THEN BREAK
                                ENDIF
                        NEXT
                ENDIF
        NEXT

        FOREACH item IN MyVar[]
                IF item.File$=Base$
                        IF LEN(item.VAR)=0
                                DELETE item
                                RETURN
                        ENDIF
                ENDIF
        NEXT
ENDFUNCTION

// set a string
FUNCTION SetStr: Name$, Base$, Value$
        LOCAL i,m
        IF Base$="" THEN RETURN ""
        IF Name$="" THEN RETURN ""
        FOREACH item IN MyVar[]
                IF item.File$=Base$
                        FOREACH item2 IN item.VAR[]
                                IF item2.Name$=Name$
                                        item2.Value$=Value$
                                        RETURN ""
                                ENDIF
                        NEXT
                ENDIF
        NEXT
        m=0
        FOREACH item IN MyVar[]
                IF item.File$=Base$
                        m=1
                        BREAK
                ENDIF
        NEXT
        IF m=0
                m=BOUNDS(MyVar[], 0)
                REDIM MyVar[m+1]
                MyVar[m].File$=Base$

        ENDIF

        FOREACH item IN MyVar[]
                IF item.File$=Base$
                        m=BOUNDS(item.VAR[], 0)
                        REDIM item.VAR[m+1]
                        item.VAR[m].Name$=Name$
                        item.VAR[m].Value$=Value$
                ENDIF
        NEXT
ENDFUNCTION


// check if file exists and also the user did not have changed anything. Do this before you do LoadStr.
FUNCTION ChechfileIni: File$
        LOCAL ok, st$,splits$[], count, hash=0

        ok=OPENFILE(1, File$, 1)
        IF ok=FALSE THEN RETURN FALSE

        LOCAL number=3423
        REPEAT
                number=number+1
                st$=LINEREAD$(1)
                st$=TRIM$(st$)
                IF TRIM$(st$)="" THEN BREAK
                hash=0
                IF LEFT$(st$, 1)="#"
                        LOCAL l$=StringField$(st$, 2, "#")
                        LOCAL r$=StringField$(st$, 3, "#")
                        IF HashString$(l$)<>r$
                                CLOSEFILE 1
                                RETURN FALSE
                        ENDIF
                ELSE
                        IF hash=0 THEN RETURN
                        ok=SPLITSTR(st$,splits$[], "|")
                        IF ok<>3
                                CLOSEFILE 1
                                RETURN FALSE
                        ENDIF
                ENDIF
        UNTIL ENDOFFILE(1)
        CLOSEFILE 1
        RETURN 1
ENDFUNCTION

// load anyting from a ini like file.
FUNCTION LoadStr: File$, Base$
        LOCAL ok, st$,splits$[], count, hash=0

        ok=ChechfileIni(File$)
        IF ok=0 THEN RETURN FALSE
        ok=OPENFILE(1, File$, 1)
        IF ok=FALSE THEN RETURN FALSE

        FOREACH item IN MyVar[]
                IF item.File$=Base$ OR Base$="all"
                        DELETE item
                ENDIF
        NEXT
        LOCAL number=3423 // used for a number based key in crypting.
        REPEAT
                count=count+1
                number=number+1
                st$=LINEREAD$(1)
                st$=TRIM$(st$)
                IF TRIM$(st$)="" THEN BREAK
                hash=0
                IF LEFT$(st$, 1)="#"
                        LOCAL l$=StringField$(st$, 2, "#")
                        LOCAL r$=StringField$(st$, 3, "#")
                        IF HashString$(l$)=r$
                                hash=1
                                st$=TRIM$(DECRYPT$("C3&7vxdfFFr3)4"+number+"/&fddsfgsd", l$)) // make sure to change this key, as well in the SaveStr.
//                      ELSE
//                              hash=0
//                              CLOSEFILE 1
//                              RETURN
                        ENDIF
                ENDIF
                IF hash=1
                        ok=SPLITSTR(st$,splits$[], "|")
                        IF ok=3
                                IF splits$[1]="" OR splits$[0]="" OR splits$[2]="" THEN BREAK
                                IF splits$[1]=Base$ OR Base$="all"
                                        SetStr(URLDECODE$(splits$[1]), URLDECODE$(splits$[0]), URLDECODE$(splits$[2]))
                                        IF count>1024 THEN BREAK
                                ENDIF
                        ENDIF
                ENDIF
        UNTIL ENDOFFILE(1)
        CLOSEFILE 1
        RETURN TRUE
ENDFUNCTION

// save all strings to a database
FUNCTION SaveStr: File$
        LOCAL ok, st$, sc$
        ok=OPENFILE(1, File$, 0)
        IF ok=FALSE THEN RETURN
        LOCAL number=342347
        FOREACH item IN MyVar[]
                FOREACH item2 IN item.VAR[]
                        number=number+1
                        st$=URLENCODE$(item.File$)+"|"+URLENCODE$(item2.Name$)+"|"+URLENCODE$(item2.Value$)
                        sc$=st$
                        st$=TRIM$(ENCRYPT$("C3&7vxdfFFr3)4"+number+"/&fddsfgsd", st$)) // make sure to change this key, as well in the LoadStr.
                        LOCAL h$=TRIM$(HashString$(st$))
                        st$="#"+st$+"#"+h$
                        WRITESTR 1, st$+"\r\n"
                NEXT
        NEXT
        CLOSEFILE 1
ENDFUNCTION


// Some Help commands (etc ReadString diddent work, due 1024 char limit)
FUNCTION LINEREAD$: FileNr
        LOCAL chda$, L$=""
        REPEAT
                IF ENDOFFILE(FileNr) THEN BREAK
                READSTR FileNr, chda$, 1
                L$=L$+chda$
        UNTIL ASC(chda$)=10
        L$=TRIM$(L$)
        RETURN L$
ENDFUNCTION

FUNCTION HashString$: Str$
        STATIC a$, ch%
        a$=""
        ch=0
        FOR i=1 TO LEN(Str$)
                a$=MID$(Str$, i, 1)
                ch=ch+ASC(a$)+i*453
        NEXT
        ch=ch*4
        a$=ch
        IF LEN(a$)>8 THEN a$=RIGHT$(a$, 8)
        RETURN a$
ENDFUNCTION

FUNCTION StringField$: TXT$, index%, Delimeter$
        LOCAL i, char$
        LOCAL RESULT$=""
        FOR i=0 TO LEN(TXT$)
                LOCAL char$=MID$(TXT$, i, 1)
                IF char$=Delimeter$ OR i=LEN(TXT$)
                        index%=index%-1
                        IF index%=0 AND i=LEN(TXT$)
                                RETURN RESULT$+char$
                        ENDIF
                        IF index%=0 THEN RETURN RESULT$
                        IF i=LEN(TXT$) THEN RETURN ""
                        RESULT$=""
                ELSE
                        RESULT$=RESULT$+char$
                ENDIF
        NEXT
        RETURN ""
ENDFUNCTION

// If you are tired to use number based images, then you can do something like below
FUNCTION imageGet: Name$
        LOCAL img
        img=GetStr$(Name$, "Sprites")
        RETURN img
ENDFUNCTION

FUNCTION imageLoad: File$, Name$, animWidth=0, animHeight=0
        LOCAL result, i,r, img, fil$
        fil$=GetStr$(Name$, "Files")
        IF fil$=File$ // if image is allready loaded, then dont load it again
                RETURN
        ENDIF

        result=GETFILESIZE(File$)
        IF result=0 THEN RETURN FALSE


        img=GetStr$(Name$, "Sprites")
        IF img<>0
                LOADSPRITE "", img
                result=img
        ELSE
                result=GENSPRITE()
        ENDIF

        IF animHeight=0
                LOADSPRITE File$, result
        ELSE
                LOADANIM File$, result, animWidth, animHeight
                SetStr(Name$+".w", "Anim", animWidth)
                SetStr(Name$+".h", "Anim", animHeight)
        ENDIF
        GETSPRITESIZE result, i, r
        IF i=0 OR r=0 THEN RETURN FALSE
        SetStr(Name$, "Sprites", result)
        SetStr(Name$, "Files", File$)
        RETURN TRUE
ENDFUNCTION

FUNCTION imageRemove: Name$
        LOCAL img, result
        img=GetStr$(Name$, "Sprites")
        LOADSPRITE "", img
        RemoveStr(Name$, "Sprites")
        RemoveStr(Name$, "Files")
ENDFUNCTION
 

You could also do a imageDraw function to draw the image, using names instead of variables.
Greedy Mouse - Karma Miwa - Spot Race - CatchOut - Android Extras - is on a vacation trip, home before end of few days in jan.

MrTAToad

  • Guest
It would be quicker to generate a hash value of a string, and search for that.

Alternatively, if you keep the strings sorted, you could then use a fast string search routine.

Offline spacefractal

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 3669
    • View Profile
    • Space Fractal
not sure what you mean?

HashFunction() I created is not bulletproff and is only designed to been used for save() and load() to prevent cheating when ingame save is used. Its actuelly too simple to use its as a unique hash value.

FindStr$() was not designed for realtime use, but more if you want to clean something up, which is why I created that function.

Its also designed to use more than one base, so its dont need to search for something alltime. Etc settings, images, sounds and such thing.

Since the game I doing not have 100 strings to search for (often only around 10-20 max), its not a problem, its mostly used for save/load things (and have used such of same thing in BlitzMax as well).
Greedy Mouse - Karma Miwa - Spot Race - CatchOut - Android Extras - is on a vacation trip, home before end of few days in jan.

MrTAToad

  • Guest
Fair enough!