]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/common/util.qc
make DB use DP_QC_URI_ESCAPE
[divverent/nexuiz.git] / data / qcsrc / common / util.qc
1 string wordwrap_buffer;
2
3 void wordwrap_buffer_put(string s)
4 {
5         wordwrap_buffer = strcat(wordwrap_buffer, s);
6 }
7
8 string wordwrap(string s, float l)
9 {
10         string r;
11         wordwrap_buffer = "";
12         wordwrap_cb(s, l, wordwrap_buffer_put);
13         r = wordwrap_buffer;
14         wordwrap_buffer = "";
15         return r;
16 }
17
18 #ifndef MENUQC
19 void wordwrap_buffer_sprint(string s)
20 {
21         wordwrap_buffer = strcat(wordwrap_buffer, s);
22         if(s == "\n")
23         {
24                 sprint(self, wordwrap_buffer);
25                 wordwrap_buffer = "";
26         }
27 }
28
29 void wordwrap_sprint(string s, float l)
30 {
31         wordwrap_buffer = "";
32         wordwrap_cb(s, l, wordwrap_buffer_sprint);
33         if(wordwrap_buffer != "")
34                 sprint(self, strcat(wordwrap_buffer, "\n"));
35         wordwrap_buffer = "";
36         return;
37 }
38 #endif
39
40 void wordwrap_cb(string s, float l, void(string) callback)
41 {
42         local string c;
43         local float lleft, i, j, wlen;
44
45         s = strzone(s);
46         lleft = l;
47         for (i = 0;i < strlen(s);i++)
48         {
49                 if (substring(s, i, 2) == "\\n")
50                 {
51                         callback("\n");
52                         lleft = l;
53                         i++;
54                 }
55                 else if (substring(s, i, 1) == "\n")
56                 {
57                         callback("\n");
58                         lleft = l;
59                 }
60                 else if (substring(s, i, 1) == " ")
61                 {
62                         if (lleft > 0)
63                         {
64                                 callback(" ");
65                                 lleft = lleft - 1;
66                         }
67                 }
68                 else
69                 {
70                         for (j = i+1;j < strlen(s);j++)
71                                 //    ^^ this skips over the first character of a word, which
72                                 //       is ALWAYS part of the word
73                                 //       this is safe since if i+1 == strlen(s), i will become
74                                 //       strlen(s)-1 at the end of this block and the function
75                                 //       will terminate. A space can't be the first character we
76                                 //       read here, and neither can a \n be the start, since these
77                                 //       two cases have been handled above.
78                         {
79                                 c = substring(s, j, 1);
80                                 if (c == " ")
81                                         break;
82                                 if (c == "\\")
83                                         break;
84                                 if (c == "\n")
85                                         break;
86                                 // we need to keep this tempstring alive even if substring is
87                                 // called repeatedly, so call strcat even though we're not
88                                 // doing anything
89                                 callback("");
90                         }
91                         wlen = j - i;
92                         if (lleft < wlen)
93                         {
94                                 callback("\n");
95                                 lleft = l;
96                         }
97                         callback(substring(s, i, wlen));
98                         lleft = lleft - wlen;
99                         i = j - 1;
100                 }
101         }
102         strunzone(s);
103 }
104
105 float dist_point_line(vector p, vector l0, vector ldir)
106 {
107         ldir = normalize(ldir);
108         
109         // remove the component in line direction
110         p = p - (p * ldir) * ldir;
111
112         // vlen of the remaining vector
113         return vlen(p);
114 }
115
116 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
117 {
118         entity e;
119         e = start;
120         funcPre(pass, e);
121         while(e.downleft)
122         {
123                 e = e.downleft;
124                 funcPre(pass, e);
125         }
126         funcPost(pass, e);
127         while(e != start)
128         {
129                 if(e.right)
130                 {
131                         e = e.right;
132                         funcPre(pass, e);
133                         while(e.downleft)
134                         {
135                                 e = e.downleft;
136                                 funcPre(pass, e);
137                         }
138                 }
139                 else
140                         e = e.up;
141                 funcPost(pass, e);
142         }
143 }
144
145 float median(float a, float b, float c)
146 {
147         if(a < c)
148                 return bound(a, b, c);
149         return bound(c, b, a);
150 }
151
152 // converts a number to a string with the indicated number of decimals
153 // works for up to 10 decimals!
154 string ftos_decimals(float number, float decimals)
155 {
156         string result;
157         string tmp;
158         float len;
159
160         // if negative, cut off the sign first
161         if(number < 0)
162                 return strcat("-", ftos_decimals(-number, decimals));
163         // it now is always positive!
164
165         // 3.516 -> 352
166         number = floor(number * pow(10, decimals) + 0.5);
167
168         // 352 -> "352"
169         result = ftos(number);
170         len = strlen(result);
171         // does it have a decimal point (should not happen)? If there is one, it is always at len-7)
172                 // if ftos had fucked it up, which should never happen: "34278.000000"
173         if(len >= 7)
174                 if(substring(result, len - 7, 1) == ".")
175                 {
176                         dprint("ftos(integer) has comma? Can't be. Affected result: ", result, "\n");
177                         result = substring(result, 0, len - 7);
178                         len -= 7;
179                 }
180                 // "34278"
181         if(decimals == 0)
182                 return result; // don't insert a point for zero decimals
183         // is it too short? If yes, insert leading zeroes
184         if(len <= decimals)
185         {
186                 result = strcat(substring("0000000000", 0, decimals - len + 1), result);
187                 len = decimals + 1;
188         }
189         // and now... INSERT THE POINT!
190         tmp = substring(result, len - decimals, decimals);
191         result = strcat(substring(result, 0, len - decimals), ".", tmp);
192         return result;
193 }
194
195 float time;
196 vector colormapPaletteColor(float c, float isPants)
197 {
198         switch(c)
199         {
200                 case  0: return '0.733 0.733 0.733';
201                 case  1: return '0.451 0.341 0.122';
202                 case  2: return '0.000 0.733 0.733';
203                 case  3: return '0.000 1.000 0.000';
204                 case  4: return '1.000 0.000 0.000';
205                 case  5: return '0.000 0.502 1.000';
206                 case  6: return '0.812 0.561 0.169';
207                 case  7: return '0.718 0.529 0.420';
208                 case  8: return '0.765 0.545 0.667';
209                 case  9: return '1.000 0.000 1.000';
210                 case 10: return '0.639 0.529 0.482';
211                 case 11: return '0.310 0.388 0.341';
212                 case 12: return '1.000 1.000 0.000';
213                 case 13: return '0.000 0.000 1.000';
214                 case 14: return '1.000 0.502 0.000';
215                 case 15:
216                         if(isPants)
217                                 return
218                                           '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))
219                                         + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))
220                                         + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));
221                         else
222                                 return
223                                           '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))
224                                         + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))
225                                         + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));
226                 default: return '0.000 0.000 0.000';
227         }
228 }
229
230 // unzone the string, and return it as tempstring. Safe to be called on string_null
231 string fstrunzone(string s)
232 {
233         string sc;
234         if not(s)
235                 return s;
236         sc = strcat(s, "");
237         strunzone(s);
238         return sc;
239 }
240
241 // Databases (hash tables)
242 #define DB_BUCKETS 8192
243 void db_save(float db, string pFilename)
244 {
245         float fh, i, n;
246         fh = fopen(pFilename, FILE_WRITE);
247         if(fh < 0)
248                 error(strcat("Can't write DB to ", pFilename));
249         n = buf_getsize(db);
250         fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
251         for(i = 0; i < n; ++i)
252                 fputs(fh, strcat(bufstr_get(db, i), "\n"));
253         fclose(fh);
254 }
255
256 float db_create()
257 {
258         return buf_create();
259 }
260
261 float db_load(string pFilename)
262 {
263         float db, fh, i, j, n;
264         string l;
265         db = buf_create();
266         if(db < 0)
267                 return -1;
268         fh = fopen(pFilename, FILE_READ);
269         if(fh < 0)
270                 return db;
271         if(stof(fgets(fh)) == DB_BUCKETS)
272         {
273                 i = 0;
274                 while((l = fgets(fh)))
275                 {
276                         if(l != "")
277                                 bufstr_set(db, i, l);
278                         ++i;
279                 }
280         }
281         else
282         {
283                 // different count of buckets?
284                 // need to reorganize the database then (SLOW)
285                 while((l = fgets(fh)))
286                 {
287                         n = tokenizebyseparator(l, "\\");
288                         for(j = 2; j < n; j += 2)
289                                 db_put(db, uri_unescape(argv(j-1)), uri_unescape(argv(j)));
290                 }
291         }
292         fclose(fh);
293         return db;
294 }
295
296 void db_dump(float db, string pFilename)
297 {
298         float fh, i, j, n, m;
299         fh = fopen(pFilename, FILE_WRITE);
300         if(fh < 0)
301                 error(strcat("Can't dump DB to ", pFilename));
302         n = buf_getsize(db);
303         fputs(fh, "0\n");
304         for(i = 0; i < n; ++i)
305         {
306                 m = tokenizebyseparator(bufstr_get(db, i), "\\");
307                 for(j = 2; j < m; j += 2)
308                         fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));
309         }
310         fclose(fh);
311 }
312
313 void db_close(float db)
314 {
315         buf_del(db);
316 }
317
318 string db_get(float db, string pKey)
319 {
320         float h;
321         pKey = uri_escape(pKey);
322         h = mod(crc16(FALSE, pKey), DB_BUCKETS);
323         return uri_unescape(infoget(bufstr_get(db, h), pKey));
324 }
325
326 void db_put(float db, string pKey, string pValue)
327 {
328         float h;
329         pKey = uri_escape(pKey);
330         pValue = uri_escape(pValue);
331         h = mod(crc16(FALSE, pKey), DB_BUCKETS);
332         bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, pValue));
333 }
334
335 void db_test()
336 {
337         float db, i;
338         print("LOAD...\n");
339         db = db_load("foo.db");
340         print("LOADED. FILL...\n");
341         for(i = 0; i < DB_BUCKETS; ++i)
342                 db_put(db, ftos(random()), "X");
343         print("FILLED. SAVE...\n");
344         db_save(db, "foo.db");
345         print("SAVED. CLOSE...\n");
346         db_close(db);
347         print("CLOSED.\n");
348 }
349
350 // Multiline text file buffers
351 float buf_load(string pFilename)
352 {
353         float buf, fh, i;
354         string l;
355         buf = buf_create();
356         if(buf < 0)
357                 return -1;
358         fh = fopen(pFilename, FILE_READ);
359         if(fh < 0)
360                 return buf;
361         i = 0;
362         while((l = fgets(fh)))
363         {
364                 bufstr_set(buf, i, l);
365                 ++i;
366         }
367         fclose(fh);
368         return buf;
369 }
370
371 void buf_save(float buf, string pFilename)
372 {
373         float fh, i, n;
374         fh = fopen(pFilename, FILE_WRITE);
375         if(fh < 0)
376                 error(strcat("Can't write buf to ", pFilename));
377         n = buf_getsize(buf);
378         for(i = 0; i < n; ++i)
379                 fputs(fh, strcat(bufstr_get(buf, i), "\n"));
380         fclose(fh);
381 }