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

Offline spacefractal

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 3904
    • 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.
Genius.Greedy Mouse - Karma Miwa - Spot Race - CatchOut - PowerUp Elevation and The beagle Jam.

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: 3904
    • 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).
Genius.Greedy Mouse - Karma Miwa - Spot Race - CatchOut - PowerUp Elevation and The beagle Jam.

MrTAToad

  • Guest
Fair enough!