FastPreferences
Verfasst: 12.07.2006 19:17
Hab das vor zwar schon vor ein paar Monaten fertig gehabt, aber nun ists
mir wieder in den Sinn gekommen, da ichs wieder mal verwenden konnte.
Diese FastPreferences heissen nicht so, weil sie extrem schnell sind,
sondern weil man sie sehr leicht und schnell in einem Programm einbauen
kann.
Gespeichert werden kann alles. Strings, Bilder, Sounds, etc. Wie in einer
Datenbank, nur nicht ganz so schnell (Datensätze werden gesucht, indem
eine Liste von Anfang an durchgegangen wird). Alle Datensätze können
über Strings (Keys wie bei Preferences von PB) angesprochen werden,
also mit Namen
Kann benutzt werden für:
- Mehrsprachensupport
- Gameressourcen-Speicherung, oder sonstiges Speichern irgendwelcher
Bilder, Sounds und Daten
- Alles, was Daten speichern muss/soll/darf
Unicode geht auch.
Der Code mit angehängtem Beispiel:
Ich hoffe es hat immer noch keine Bugs 
Update:
- Speed und Bug fixed
- Neue Methoden
mir wieder in den Sinn gekommen, da ichs wieder mal verwenden konnte.
Diese FastPreferences heissen nicht so, weil sie extrem schnell sind,
sondern weil man sie sehr leicht und schnell in einem Programm einbauen
kann.
Gespeichert werden kann alles. Strings, Bilder, Sounds, etc. Wie in einer
Datenbank, nur nicht ganz so schnell (Datensätze werden gesucht, indem
eine Liste von Anfang an durchgegangen wird). Alle Datensätze können
über Strings (Keys wie bei Preferences von PB) angesprochen werden,
also mit Namen

Kann benutzt werden für:
- Mehrsprachensupport
- Gameressourcen-Speicherung, oder sonstiges Speichern irgendwelcher
Bilder, Sounds und Daten
- Alles, was Daten speichern muss/soll/darf
Unicode geht auch.
Der Code mit angehängtem Beispiel:
Code: Alles auswählen
; PB 4.0
; Coded by Remi_Meier@gmx.ch in 2006
; If you want to be a thief, just remove this comment.
EnableExplicit
Structure FPREFELEM
*prev.FPREFELEM
*next.FPREFELEM
*name ; Ptr to String
*value ; Ptr to Data
EndStructure
Interface iFPref
addData.l(key.s, *mem, len.l) ; Adds data identified with a key
addString.l(key.s, string.s) ; Adds a string to the list
addChar.l(key.s, Char.c) ; Adds a character to the list
addLong.l(key.s, long.l) ; Adds a long to the list
addQuad.l(key.s, quad.q) ; Adds a quad to the list
addFloat.l(key.s, float.f) ; Adds a float to the list
addDouble.l(key.s, double.d) ; Adds a double to the list
removeData.l(key.s) ; Removes the data identified with the key
getDataPtr.l(key.s) ; Gets a pointer to the data
getString.s(key.s) ; Returns the data as a string, be sure, that Data is a String!
getChar.c(key.s) ; returns the data as a character
getLong.l(key.s) ; returns the data as a long
getQuad.q(key.s) ; returns the data as a quad
getFloat.f(key.s) ; returns the data as a float
getDouble.d(key.s) ; returns the data as a double
clear.l() ; clears the list
saveToFile.l(file.s) ; Save data list to file
loadFromFile.l(file.s) ; Loads data from file (adds it to the list!)
; Iteration
examineData() ; Starts the iteration through the data pointers
nextData.l() ; Selects next data element, returns 0 if none exists
currentDataPtr.l() ; Returns a pointer to the data in the currently selected element
currentString.s() ; Returns the string of currentDataPtr(), be sure, that Data is a String!
currentKey.s() ; Returns the key of the currently selected element
countKeys.l() ; returns the count of stored keys
EndInterface
Structure FPREFKEY
hash.l ; CRC32FingerPrint
*element.FPREFELEM ; ptrToElement
EndStructure
Structure cFPref
*VT
kCount.l ; count of keys
*keys.FPREFKEY ; Array of FPREFKEYs
*listHead.FPREFELEM ; Ptr to ListStart
*currentData.FPREFELEM ; for Iterator
EndStructure
Procedure FPref__freeElement(*this.cFPref, *elem.FPREFELEM)
If *elem
If *elem\Name
FreeMemory(*elem\Name)
EndIf
If *elem\Value
FreeMemory(*elem\Value)
EndIf
FreeMemory(*elem)
EndIf
EndProcedure
Procedure FPref__addKey(*this.cFPref, *name, *element)
Protected *t.FPREFKEY
If *name And *element
*this\kCount + 1
*this\keys = ReAllocateMemory(*this\keys, *this\kCount * SizeOf(FPREFKEY))
*t = *this\keys + (*this\kCount - 1) * SizeOf(FPREFKEY)
*t\hash = CRC32Fingerprint(*name, MemoryStringLength(*name) * SizeOf(CHARACTER))
*t\element = *element
EndIf
EndProcedure
Procedure FPref__removeKey(*this.cFPref, *key.FPREFKEY)
Protected newSize.l
If *key And *this\keys
*this\kCount - 1
newSize = *this\kCount * SizeOf(FPREFKEY)
MoveMemory(*key + SizeOf(FPREFKEY), *key, *this\keys + newSize - *key)
*this\keys = ReAllocateMemory(*this\keys, newSize)
EndIf
EndProcedure
Procedure.l FPref__addElement(*this.cFPref, *elem.FPREFELEM)
Protected *key.FPREFKEY, found.l, *t.FPREFKEY, z.l, namehash.l
If Not *elem Or Not *elem\Name
FPref__freeElement(*this, *elem)
Else
If *this\listHead And *this\keys
namehash = CRC32Fingerprint(*elem\Name, MemoryStringLength(*elem\Name) * SizeOf(CHARACTER))
*key = *this\keys
found = #False
For z = 1 To *this\kCount
If *key\hash = namehash And PeekS(*key\element\Name) = PeekS(*elem\Name)
; Modify Data
If *key\element\Value : FreeMemory(*key\element\Value) : EndIf
*key\element\Value = *elem\Value
found = #True
Break
EndIf
*key + SizeOf(FPREFKEY)
Next
If found
FreeMemory(*elem\Name)
FreeMemory(*elem)
ProcedureReturn #True
Else
; Else add at start
*elem\Next = *this\listHead
*elem\Prev = 0
*this\listHead\Prev = *elem
*this\listHead = *elem
; Add to Keys-List
FPref__addKey(*this, *elem\Name, *elem)
EndIf
Else
; Else insert at start
*elem\Next = 0
*elem\Prev = 0
*this\listHead = *elem
FPref__addKey(*this, *elem\Name, *elem)
EndIf
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
Procedure.l FPref__removeElement(*this.cFPref, *elem.FPREFELEM)
Protected *key.FPREFKEY, z.l, namehash.l
If Not *elem Or Not *elem\Name Or Not *this\listHead Or Not *this\keys
FPref__freeElement(*this, *elem)
Else
namehash = CRC32Fingerprint(*elem\Name, MemoryStringLength(*elem\Name) * SizeOf(CHARACTER))
*key = *this\keys
For z = 1 To *this\kCount
If *key\hash = namehash And PeekS(*key\element\Name) = PeekS(*elem\Name)
; Remove it
If Not *key\element\Prev
*this\listHead = *key\element\Next
Else
*key\element\Prev\Next = *key\element\Next
EndIf
If *key\element\Next
*key\element\Next\Prev = *key\element\Prev
EndIf
FPref__freeElement(*this, *key\element)
FPref__freeElement(*this, *elem)
FPref__removeKey(*this, *key)
ProcedureReturn #True
EndIf
*key + SizeOf(FPREFKEY)
Next
FPref__freeElement(*this, *elem)
EndIf
ProcedureReturn #False
EndProcedure
Procedure.l FPref__createElement(*this.cFPref, key.s, *mem, len.l)
Protected *elem.FPREFELEM
*elem = AllocateMemory(SizeOf(FPREFELEM))
If Not *elem
FPref__freeElement(*this, *elem)
ProcedureReturn #False
EndIf
If Len(key) > 0
*elem\Name = AllocateMemory((Len(key) + 1) * SizeOf(CHARACTER))
If Not *elem\Name
FPref__freeElement(*this, *elem)
ProcedureReturn #False
EndIf
EndIf
If len > 0 And *mem
*elem\Value = AllocateMemory(len)
If Not *elem\Value
FPref__freeElement(*this, *elem)
ProcedureReturn #False
EndIf
EndIf
If Len(key) > 0
CopyMemory(@key, *elem\Name, (Len(key) + 1) * SizeOf(CHARACTER))
EndIf
If len > 0 And *mem
CopyMemory(*mem, *elem\Value, len)
EndIf
ProcedureReturn *elem
EndProcedure
;** fpref_addData
;* adds or modifies data of the key.
;** .key: a key to identify the data
;** .*mem: the memory from which to copy
;** .len: len to copy from *mem
Procedure.l FPref_addData(*this.cFPref, key.s, *mem, len.l)
Protected *elem.FPREFELEM
*elem = FPref__createElement(*this, key, *mem, len)
If FPref__addElement(*this, *elem)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
;** fpref_addstring
;* a simplyfied version of addData to add a string
Procedure.l FPref_addString(*this.cFPref, key.s, string.s)
ProcedureReturn FPref_addData(*this, key, @string, (Len(string) + 1) * SizeOf(CHARACTER))
EndProcedure
;** fpref_removedata
;* removes the element identified by the key
Procedure.l FPref_removeData(*this.cFPref, key.s)
Protected *elem.FPREFELEM
*elem = FPref__createElement(*this, key, 0, 0)
If FPref__removeElement(*this, *elem)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
;** fpref_getdataptr
;* returns a pointer to the data of the key
Procedure.l FPref_getDataPtr(*this.cFPref, key.s)
Protected *key.FPREFKEY, z.l, namehash.l
If key And *this\keys
namehash = CRC32Fingerprint(@key, Len(key) * SizeOf(CHARACTER))
*key = *this\keys
For z = 1 To *this\kCount
If *key\hash = namehash And PeekS(*key\element\Name) = key
ProcedureReturn *key\element\Value
EndIf
*key + SizeOf(FPREFKEY)
Next
EndIf
ProcedureReturn #Null
EndProcedure
;** fpref_getstring
;* returns the string identified by the key_
;* simplified version of getdataptr() with PeekS()_
;* <b>Only works if you really saved a string in this key!</b>
Procedure.s FPref_getString(*this.cFPref, key.s)
Protected *mem
*mem = FPref_getDataPtr(*this, key)
If *mem
ProcedureReturn PeekS(*mem)
Else
ProcedureReturn ""
EndIf
EndProcedure
Procedure.l FPref_getLong(*this.cFPref, key.s)
Protected *mem
*mem = FPref_getDataPtr(*this, key)
If *mem
ProcedureReturn PeekL(*mem)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure.l FPref_addLong(*this.cFPref, key.s, long.l)
ProcedureReturn FPref_addData(*this, key, @long, SizeOf(Long))
EndProcedure
Procedure.c FPref_getChar(*this.cFPref, key.s)
Protected *mem
*mem = FPref_getDataPtr(*this, key)
If *mem
ProcedureReturn PeekC(*mem)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure.l FPref_addChar(*this.cFPref, key.s, Char.c)
ProcedureReturn FPref_addData(*this, key, @Char, SizeOf(CHARACTER))
EndProcedure
Procedure.f FPref_getFloat(*this.cFPref, key.s)
Protected *mem
*mem = FPref_getDataPtr(*this, key)
If *mem
ProcedureReturn PeekF(*mem)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure.l FPref_addFloat(*this.cFPref, key.s, float.f)
ProcedureReturn FPref_addData(*this, key, @float, SizeOf(Float))
EndProcedure
Procedure.q FPref_getQuad(*this.cFPref, key.s)
Protected *mem
*mem = FPref_getDataPtr(*this, key)
If *mem
ProcedureReturn PeekQ(*mem)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure.l FPref_addQuad(*this.cFPref, key.s, quad.q)
ProcedureReturn FPref_addData(*this, key, @quad, SizeOf(Quad))
EndProcedure
Procedure.d FPref_getDouble(*this.cFPref, key.s)
Protected *mem
*mem = FPref_getDataPtr(*this, key)
If *mem
ProcedureReturn PeekD(*mem)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure.l FPref_addDouble(*this.cFPref, key.s, double.d)
ProcedureReturn FPref_addData(*this, key, @double, SizeOf(Double))
EndProcedure
;** fpref_clear: deletes all keys
Procedure.l FPref_clear(*this.cFPref)
Protected *list.FPREFELEM, *nextelem.FPREFELEM
If *this\listHead And *this\keys
*list = *this\listHead
While *list
*nextelem = *list\Next
FPref__freeElement(*this, *list)
*list = *nextelem
Wend
*this\listHead = 0
FreeMemory(*this\keys)
*this\kCount = 0
EndIf
ProcedureReturn #True
EndProcedure
;** fpref_savetofile: saves all keys in the list
;* to a file (binary)
Procedure.l FPref_saveToFile(*this.cFPref, file.s)
Protected fileID.l, *list.FPREFELEM, len.l
fileID = CreateFile(#PB_Any, file)
If IsFile(fileID)
If *this\listHead
*list = *this\listHead
While *list
If *list\Name
len = MemorySize(*list\Name)
WriteLong(fileID, len)
WriteData(fileID, *list\Name, len)
If *list\Name
len = MemorySize(*list\Value)
Else
len = 0
EndIf
WriteLong(fileID, len)
If len
WriteData(fileID, *list\Value, len)
EndIf
EndIf
*list = *list\Next
Wend
EndIf
CloseFile(fileID)
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
;** fpref_loadfromfile: loads a previously saved list
;* from a file. If the list already contains some
;* keys, they will be overwritten (if the same Key.s used)
;* and the new keys will be added. No keys will be removed!
Procedure.l FPref_loadFromFile(*this.cFPref, file.s)
Protected fileID.l, *elem.FPREFELEM, len.l
fileID = ReadFile(#PB_Any, file)
If IsFile(fileID)
While Not Eof(fileID)
*elem = AllocateMemory(SizeOf(FPREFELEM))
If Not *elem
ProcedureReturn #False
EndIf
len = ReadLong(fileID)
*elem\Name = AllocateMemory(len)
If *elem\Name
ReadData(fileID, *elem\Name, len)
Else
FreeMemory(*elem)
ProcedureReturn #False
EndIf
len = ReadLong(fileID)
If len
*elem\Value = AllocateMemory(len)
If Not *elem\Value
FreeMemory(*elem\Name)
FreeMemory(*elem)
ProcedureReturn #False
EndIf
ReadData(fileID, *elem\Value, len)
EndIf
If Not FPref__addElement(*this, *elem)
ProcedureReturn #False
EndIf
Wend
CloseFile(fileID)
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
;** fpref_examinedata: Starts examination of all keys in the list
Procedure FPref_examineData(*this.cFPref)
*this\currentData = 0
EndProcedure
;** fpref_nextdata:
;* Goes to the next key (returns 0 if there is none)
Procedure.l FPref_nextData(*this.cFPref)
If *this\currentData = 0
*this\currentData = *this\listHead
Else
*this\currentData = *this\currentData\Next
EndIf
ProcedureReturn *this\currentData
EndProcedure
;** fpref_currentdataptr
;* returns the dataptr of the current element/key
Procedure.l FPref_currentDataPtr(*this.cFPref)
If *this\currentData
ProcedureReturn *this\currentData\Value
Else
ProcedureReturn #False
EndIf
EndProcedure
;** fpref_currentString
;* returns a string of the current element/key.
;* simplified version of currentDataPtr()._
;* <b>Only valid if there is really a string
;* stored in this key!</b>_
;* To be aware of this, you could i. c. add a
;* prefix to all of your keys that identifies
;* of which type the data in this key is. Like:_
;* Key.s = "s CancelButton"
Procedure.s FPref_currentString(*this.cFPref)
Protected *mem
*mem = FPref_currentDataPtr(*this)
If *mem
ProcedureReturn PeekS(*mem)
Else
ProcedureReturn ""
EndIf
EndProcedure
;** fpref_currentkey
;* returns the key/identifier of the current element/key.
Procedure.s FPref_currentKey(*this.cFPref)
If *this\currentData
ProcedureReturn PeekS(*this\currentData\Name)
Else
ProcedureReturn ""
EndIf
EndProcedure
Procedure.l FPref_CountKeys(*this.cFPref)
ProcedureReturn *this\kCount
EndProcedure
DataSection ;[
cFPref_VT:
Data.l @FPref_addData(), @FPref_addString(), @FPref_addChar(), @FPref_addLong()
Data.l @FPref_addQuad(), @FPref_addFloat(), @FPref_addDouble(), @FPref_removeData()
Data.l @FPref_getDataPtr(), @FPref_getString(), @FPref_getChar(),@FPref_getLong()
Data.l @FPref_getQuad(), @FPref_getFloat(), @FPref_getDouble(), @FPref_clear()
Data.l @FPref_saveToFile(), @FPref_loadFromFile(), @FPref_examineData()
Data.l @FPref_nextData(), @FPref_currentDataPtr(), @FPref_currentString()
Data.l @FPref_currentKey(), @FPref_CountKeys()
EndDataSection ;]
Procedure.l new_FPref()
Protected *this.cFPref
*this = AllocateMemory(SizeOf(cFPref))
If *this = 0 : ProcedureReturn 0 : EndIf
*this\vt = ?cFPref_VT
ProcedureReturn *this
EndProcedure
Procedure.l delete_FPref(*this.cFPref)
FPref_clear(*this)
FreeMemory(*this)
EndProcedure
;
; Define.iFPref daten
; daten = new_FPref()
;
; Define.l z
; Dim toSave.l(2)
; toSave(0) = 1
; toSave(1) = 2
; toSave(2) = 3
;
; daten\addString("s Hilfe", "Hilfe")
; daten\addString("s Datei", "Datei")
; daten\addString("s Bearbeiten", "Bearbeiten")
; daten\addData("d Array", @toSave(), 3 * 4)
; daten\saveToFile("german.data")
;
; toSave(0) = 4
; toSave(1) = 5
; toSave(2) = 6
; daten\addString("s Hilfe", "Help")
; daten\addString("s Datei", "File")
; daten\addString("s Bearbeiten", "Edit")
; daten\addData("d Array", @toSave(), 3 * 4)
; daten\saveToFile("english.data")
;
;
; Debug "###### German:"
; daten\loadFromFile("german.data")
; daten\examineData()
; While daten\nextData()
; If Left(daten\currentKey(), 1) = "s"
; Debug daten\currentKey() + ": " + daten\currentString()
; Else
; CopyMemory(daten\currentDataPtr(), @toSave(), MemorySize(daten\currentDataPtr()))
; Debug daten\currentKey() + ":"
; For z = 0 To 2
; Debug toSave(z)
; Next
; EndIf
; Wend
;
; Debug ""
;
; Debug "###### English:"
; daten\loadFromFile("english.data")
; daten\examineData()
; While daten\nextData()
; If Left(daten\currentKey(), 1) = "s"
; Debug daten\currentKey() + ": " + daten\currentString()
; Else
; CopyMemory(daten\currentDataPtr(), @toSave(), MemorySize(daten\currentDataPtr()))
; Debug daten\currentKey() + ":"
; For z = 0 To 2
; Debug toSave(z)
; Next
; EndIf
; Wend
;
; delete_FPref(daten)

Update:
- Speed und Bug fixed

- Neue Methoden