-
Notifications
You must be signed in to change notification settings - Fork 3
/
clsIniReader.cls
447 lines (374 loc) · 15.1 KB
/
clsIniReader.cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "clsIniReader"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'**************************************************************
' clsIniReader.cls - Loads INI files into memory and applies Binary Search to get values at high speed.
' Use it instead of GetVar when reading several values form the same file at once, otherwise it's not usefull.
' Based in the idea of AlejoLP and his clsLeerInis.
'
' Designed and implemented by Juan Martín Sotuyo Dodero (Maraxus)
' (juansotuyo@gmail.com)
'**************************************************************
'**************************************************************************
'This program is free software; you can redistribute it and/or modify
'it under the terms of the Affero General Public License;
'either version 1 of the License, or any later version.
'
'This program is distributed in the hope that it will be useful,
'but WITHOUT ANY WARRANTY; without even the implied warranty of
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
'Affero General Public License for more details.
'
'You should have received a copy of the Affero General Public License
'along with this program; if not, you can find it at http://www.affero.org/oagpl.html
'**************************************************************************
''
'Loads a complete INI file into memory and sorts it's data and keys for faster searches.
'It is MUCH faster than GetPrivateProfileStringA if you search for several values within a file, otherwise stick to the API.
'It's particularly usefull when you can keep a whole file in memory such as NPCs.dat'
' Based in the idea of AlejoLP and his clsLeerInis.
'
' @author Juan Martín Sotuyo Dodero (Maraxus) juansotuyo@gmail.com
' @version 1.1.0
' @date 20060501
'01/05/2006 - Juan Martín Sotuyo Dodero (Maraxus) - (juansotuyo@gmail.com)
' - First Release
'
'01/04/2008 - Juan Martín Sotuyo Dodero (Maraxus) - (juansotuyo@gmail.com)
' - Add: KeyExists method allows to check for valid section keys.
Option Explicit
Option Base 0
''
'Structure that contains a value and it's key in a INI file
'
' @param key String containing the key associated to the value.
' @param value String containing the value of the INI entry.
' @see MainNode
'
Private Type ChildNode
key As String
value As String
End Type
''
'Structure that contains all info under a tag in a INI file.
'Such tags are indicated with the "[" and "]" characters.
'
' @param name String containing the text within the "[" and "]" characters.
'It's the key used when searching for a main section of the INI data.
' @param values Array of ChildNodes, each containing a value entry along with it's key.
' @param numValues Number of entrys in the main node.
Private Type MainNode
name As String
values() As ChildNode
numValues As Long
End Type
''
'Containts all Main sections of the loaded INI file
Private fileData() As MainNode
''
'Stores the total number of main sections in the loaded INI file
Private MainNodes As Long
''
'Default constructor. Does nothing.
Private Sub Class_Initialize()
'**************************************************************
'Author: Juan Martín Sotuyo Dodero
'Last Modify Date: 5/01/2006
'
'**************************************************************
End Sub
''
'Destroy every array and deallocates al memory.
'
Private Sub Class_Terminate()
'**************************************************************
'Author: Juan Martín Sotuyo Dodero
'Last Modify Date: 5/01/2006
'
'**************************************************************
Dim i As Long
'Clean up
If MainNodes Then
For i = 1 To MainNodes - 1
Erase fileData(i).values
Next i
Erase fileData
End If
MainNodes = 0
End Sub
''
'Loads a INI file so it's values can be read. Must be called before being able to use GetValue.
'
' @param file Complete path of the INI file to be loaded.
' @see GetValue
Public Sub Initialize(ByVal file As String)
'**************************************************************
'Author: Juan Martín Sotuyo Dodero
'Last Modify Date: 27/07/2006
'Opens the requested file and loads it's data into memory
'**************************************************************
Dim handle As Integer
Dim Text As String
Dim Pos As Long
'Prevent memory losses if we are attempting to reload a file....
Call Class_Terminate
'Get a free handle and start reading line by line until the end
handle = FreeFile
Open file For Input As handle
Do Until EOF(handle)
Line Input #handle, Text
'Is it null??
If Len(Text) Then
'If it starts with '[' it is a main node or nothing (GetPrivateProfileStringA works this way), otherwise it's a value
If Left$(Text, 1) = "[" Then
'If it has an ending ']' it's a main node, otherwise it's nothing
Pos = InStr(2, Text, "]")
If Pos Then
'Add a main node
ReDim Preserve fileData(MainNodes) As MainNode
fileData(MainNodes).name = UCase$(Trim$(mid$(Text, 2, Pos - 2)))
MainNodes = MainNodes + 1
End If
Else
'So it's a value. Check if it has a '=', otherwise it's nothing
Pos = InStr(2, Text, "=")
If Pos Then
'Is it under any main node??
If MainNodes Then
With fileData(MainNodes - 1)
'Add it to the main node's value
ReDim Preserve .values(.numValues) As ChildNode
.values(.numValues).value = Right$(Text, Len(Text) - Pos)
.values(.numValues).key = UCase$(Left$(Text, Pos - 1))
.numValues = .numValues + 1
End With
End If
End If
End If
End If
Loop
Close handle
Dim i As Long
If MainNodes Then
'Sort main nodes to allow binary search
Call SortMainNodes(0, MainNodes - 1)
'Sort values of each node to allow binary search
For i = 0 To MainNodes - 1
If fileData(i).numValues Then _
Call SortChildNodes(fileData(i), 0, fileData(i).numValues - 1)
Next i
End If
End Sub
''
'Sorts all child nodes within the given MainNode alphabetically by their keys. Uses quicksort.
'
' @param Node The MainNode whose values are to be sorted.
' @param first The first index to consider when sorting.
' @param last The last index to be considered when sorting.
Private Sub SortChildNodes(ByRef Node As MainNode, ByVal First As Long, ByVal Last As Long)
'**************************************************************
'Author: Juan Martín Sotuyo Dodero
'Last Modify Date: 5/01/2006
'Sorts the list of values in a given MainNode using quicksort,
'this allows the use of Binary Search for faster searches
'**************************************************************
Dim min As Long 'First item in the list
Dim max As Long 'Last item in the list
Dim comp As String 'Item used to compare
Dim temp As ChildNode
min = First
max = Last
With Node
comp = .values((min + max) \ 2).key
Do While min <= max
Do While .values(min).key < comp And min < Last
min = min + 1
Loop
Do While .values(max).key > comp And max > First
max = max - 1
Loop
If min <= max Then
temp = .values(min)
.values(min) = .values(max)
.values(max) = temp
min = min + 1
max = max - 1
End If
Loop
End With
If First < max Then SortChildNodes Node, First, max
If min < Last Then SortChildNodes Node, min, Last
End Sub
''
'Sorts all main nodes in the loaded INI file alphabetically by their names. Uses quicksort.
'
' @param first The first index to consider when sorting.
' @param last The last index to be considered when sorting.
Private Sub SortMainNodes(ByVal First As Integer, ByVal Last As Integer)
'**************************************************************
'Author: Juan Martín Sotuyo Dodero
'Last Modify Date: 5/01/2006
'Sorts the MainNodes list using quicksort,
'this allows the use of Binary Search for faster searches
'**************************************************************
Dim min As Long 'First item in the list
Dim max As Long 'Last item in the list
Dim comp As String 'Item used to compare
Dim temp As MainNode
min = First
max = Last
comp = fileData((min + max) \ 2).name
Do While min <= max
Do While fileData(min).name < comp And min < Last
min = min + 1
Loop
Do While fileData(max).name > comp And max > First
max = max - 1
Loop
If min <= max Then
temp = fileData(min)
fileData(min) = fileData(max)
fileData(max) = temp
min = min + 1
max = max - 1
End If
Loop
If First < max Then SortMainNodes First, max
If min < Last Then SortMainNodes min, Last
End Sub
''
'Searches for a given key within a given main section and if it exists retrieves it's value, otherwise a null string
'
' @param Main The name of the main section in which we will be searching.
' @param key The key of the value we are looking for.
' @returns The value asociated with the given key under the requeted main section of the INI file or a null string if it's not found.
Public Function GetValue(ByVal Main As String, ByVal key As String) As String
'**************************************************************
'Author: Juan Martín Sotuyo Dodero
'Last Modify Date: 5/01/2006
'Returns a value if the key and main node exist, or a nullstring otherwise
'**************************************************************
Dim i As Long
Dim j As Long
'Search for the main node
i = FindMain(UCase$(Main))
If i >= 0 Then
'If valid, binary search among keys
j = FindKey(fileData(i), UCase$(key))
'If we found it we return it
If j >= 0 Then GetValue = fileData(i).values(j).value
End If
End Function
''
'Searches for a given key within a given main section and if it exists retrieves it's value, otherwise a null string
'
' @param Main The name of the main section in which we will be searching.
' @param key The key of the value we are looking for.
' @returns The value asociated with the given key under the requeted main section of the INI file or a null string if it's not found.
Public Sub ChangeValue(ByVal Main As String, ByVal key As String, ByVal value As Long)
'**************************************************************
'Author: ZaMa
'Last Modify Date: 27/05/2009
'If the key and main node exist, changes the value
'**************************************************************
Dim i As Long
Dim j As Long
'Search for the main node
i = FindMain(UCase$(Main))
If i >= 0 Then
'If valid, binary search among keys
j = FindKey(fileData(i), UCase$(key))
'If we found it we change it
If j >= 0 Then fileData(i).values(j).value = value
End If
End Sub
''
'Searches for a given key within a given main node and returns the index in which it's stored or the negation of the index in which it should be if not found.
'
' @param Node The MainNode among whose value entries we will be searching.
' @param key The key of the value we are looking for.
' @returns The index in which the value with the key we are looking for is stored or the negation of the index in which it should be if not found.
Private Function FindKey(ByRef Node As MainNode, ByVal key As String) As Long
'**************************************************************
'Author: Juan Martín Sotuyo Dodero
'Last Modify Date: 5/01/2006
'Returns the index of the value which key matches the requested one,
'or the negation of the position were it should be if not found
'**************************************************************
Dim min As Long
Dim max As Long
Dim mid As Long
min = 0
max = Node.numValues - 1
Do While min <= max
mid = (min + max) \ 2
If Node.values(mid).key < key Then
min = mid + 1
ElseIf Node.values(mid).key > key Then
max = mid - 1
Else
'We found it
FindKey = mid
Exit Function
End If
Loop
'Not found, return the negation of the position where it should be
'(all higher values are to the right of the list and lower values are to the left)
FindKey = Not mid
End Function
''
'Searches for a main section with the given name within the loaded INI file and returns the index in which it's stored or the negation of the index in which it should be if not found.
'
' @param name The name of the MainNode we are looking for.
' @returns The index in which the main section we are looking for is stored or the negation of the index in which it should be if not found.
Private Function FindMain(ByVal name As String) As Long
'**************************************************************
'Author: Juan Martín Sotuyo Dodero
'Last Modify Date: 5/01/2006
'Returns the index of the MainNode which name matches the requested one,
'or the negation of the position were it should be if not found
'**************************************************************
Dim min As Long
Dim max As Long
Dim mid As Long
min = 0
max = MainNodes - 1
Do While min <= max
mid = (min + max) \ 2
If fileData(mid).name < name Then
min = mid + 1
ElseIf fileData(mid).name > name Then
max = mid - 1
Else
'We found it
FindMain = mid
Exit Function
End If
Loop
'Not found, return the negation of the position where it should be
'(all higher values are to the right of the list and lower values are to the left)
FindMain = Not mid
End Function
''
'Checks wether a given key exists or not.
'
' @param name The name of the element whose existance is being checked.
' @returns True if the key exists, false otherwise.
Public Function KeyExists(ByVal name As String) As Boolean
'**************************************************************
'Author: Juan Martín Sotuyo Dodero
'Last Modify Date: 04/01/2008
'Returns true of the key exists, false otherwise.
'**************************************************************
KeyExists = FindMain(UCase$(name)) >= 0
End Function