ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/freezethaw.C
Revision: 1.8
Committed: Thu Feb 1 19:15:39 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.7: +47 -2 lines
Log Message:
- improve error messages from object_thawer to incldue line numbers
- provide geenric parse error handler
- finish basic design of generic object loader
- implement generic regions loader
- use it to load regions:
  loader_region loader;
  if (!loader.load (filename))
    error;
- regions should now be the very first filetype that could be reloaded at runtime

File Contents

# User Rev Content
1 root 1.1 /*****************************************************************************/
2     /* CrossFire, A roguelike realtime multiplayer game */
3     /*****************************************************************************/
4    
5     /*
6     * This code is placed under the GNU General Public Licence (GPL)
7     *
8     * Copyright (C) 2006 by Marc Lehmann <crossfire@schmorp.de>
9     *
10     * This program is free software; you can redistribute it and/or modify
11     * it under the terms of the GNU General Public License as published by
12     * the Free Software Foundation; either version 2 of the License, or
13     * (at your option) any later version.
14     *
15     * This program is distributed in the hope that it will be useful,
16     * but WITHOUT ANY WARRANTY; without even the implied warranty of
17     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18     * GNU General Public License for more details.
19     *
20     * You should have received a copy of the GNU General Public License
21     * along with this program; if not, write to the Free Software
22     * Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23     */
24    
25 root 1.4 #include "global.h" // bug in cfperl.h, doesn't include interface_class stuff
26 root 1.1 #include "logger.h"
27     #include "cfperl.h"
28     #include "kw_hash.h"
29    
30     object_freezer::object_freezer ()
31     : dynbuf (128 * 1024, 64 * 1024)
32     {
33     av = newAV ();
34     }
35    
36     object_freezer::~object_freezer ()
37     {
38     SvREFCNT_dec (av);
39     }
40    
41 root 1.4 void
42     object_freezer::put (attachable *ext)
43 root 1.1 {
44     ext->optimise ();
45    
46     if (ext->self)
47     {
48     int idx = AvFILLp ((AV *)av) + 1;
49     av_store (av, idx, newRV_inc ((SV *)ext->self));
50    
51     add ((void *)"oid ", 4);
52     add ((sint32)idx);
53     add ('\n');
54     }
55     }
56    
57 root 1.4 bool
58     object_freezer::save (const char *path)
59 root 1.1 {
60     CALL_BEGIN (3);
61 root 1.4 CALL_ARG_SV (newSVpv (path, 0));
62 root 1.1 CALL_ARG_SV (newRV_noinc (newSVpvn ((char *)linearise (), size ())));
63     CALL_ARG_SV (newRV_inc ((SV *)av));
64     CALL_CALL ("cf::object_freezer_save", G_VOID | G_DISCARD);
65     CALL_END;
66     }
67    
68 root 1.4 char *
69     object_freezer::as_string ()
70 root 1.1 {
71     CALL_BEGIN (2);
72     CALL_ARG_SV (newRV_noinc (newSVpvn ((char *)linearise (), size ())));
73     CALL_ARG_SV (newRV_inc ((SV *)av));
74     CALL_CALL ("cf::object_freezer_as_string", G_SCALAR);
75    
76     char *res = count > 0
77 root 1.3 ? strdup (SvPVX (POPs))
78 root 1.1 : strdup ("[fatal error]");
79    
80     CALL_END;
81    
82     return res;
83     }
84    
85 root 1.4 int
86     fprintf (object_freezer &freezer, const char *format, ...)
87 root 1.1 {
88     va_list ap;
89    
90     va_start (ap, format);
91    
92     int len = vsnprintf ((char *)freezer.force (1024), 1024, format, ap);
93    
94     if (len >= 0)
95     freezer.alloc (len);
96    
97     va_end (ap);
98     }
99    
100 root 1.4 int
101     fputs (const char *s, object_freezer &freezer)
102 root 1.1 {
103     freezer.add (s);
104     }
105    
106     static const char thawer_eof[] = "\n\n\n\0\0\0";
107    
108 root 1.4 object_thawer::object_thawer (const char *path)
109     : name (strdup (path))
110 root 1.1 {
111     static const char eof[] = "\n\n\n\0\0\0";
112    
113     av = 0;
114     text = 0;
115     line = 0;
116 root 1.8 linenum = 0;
117 root 1.1
118 root 1.4 if (path)
119 root 1.1 {
120     CALL_BEGIN (1);
121 root 1.4 CALL_ARG_SV (newSVpv (path, 0));
122 root 1.1 CALL_CALL ("cf::object_thawer_load", G_ARRAY);
123    
124     if (count == 2)
125     {
126     // second value - perl objects
127     {
128     SV *sv = POPs;
129     if (SvROK (sv))
130     av = (AV *)SvREFCNT_inc (SvRV (sv));
131     }
132    
133     // first value - text part, pad with 3 zeroes
134     {
135     SV *sv = POPs;
136     STRLEN len;
137     char *sv_ = SvPVbyte (sv, len);
138     text = newSV (len + sizeof (eof));
139     SvCUR_set (text, len);
140     memcpy (SvPVX (text), sv_, len);
141     memcpy (SvEND (text), eof, sizeof (eof)); // just to be sure
142    
143     line = SvPVX (text);
144     }
145     }
146    
147     CALL_END;
148     }
149     }
150    
151     object_thawer::object_thawer (const char *data, AV *perlav)
152 root 1.4 : name (strdup ("(memory stream"))
153 root 1.1 {
154     av = perlav;
155     text = newSVpv (data, 0);
156     sv_catpv (text, thawer_eof);
157     line = SvPVbyte_nolen (text);
158     }
159    
160 root 1.4 void
161     object_thawer::get (attachable *obj, int oid)
162 root 1.1 {
163     if (!av || oid < 0) // this is actually an error of sorts
164     return;
165    
166     SV **svp = av_fetch ((AV *)av, oid, 0);
167    
168     if (!svp || !SvROK (*svp))
169     {
170     printf ("trying to thaw duplicate or never-issued oid %d, ignoring.\n", oid);
171     return;
172     }
173    
174     if (!SvROK (*svp))
175     {
176     LOG (llevError, "deserialised perl object is not an RV");
177     return;
178     }
179    
180     HV *hv = (HV *)SvRV (*svp);
181    
182     if (SvTYPE (hv) != SVt_PVHV)
183     {
184     LOG (llevError, "deserialised perl object is not a PVHV");
185     return;
186     }
187    
188     if (obj->self)
189     {
190     // the hard way(?)
191    
192 root 1.6 CALL_BEGIN (2);
193     CALL_ARG_SV (newRV_inc ((SV *)obj->self));
194     CALL_ARG_SV (newRV_inc ((SV *)hv));
195     PUTBACK;
196     call_method ("thawer_merge", G_DISCARD | G_EVAL);
197     SPAGAIN;
198     CALL_END;
199 root 1.1 }
200     else
201     {
202     // the easy way(?)
203    
204     obj->self = hv;
205     SvRV_set (*svp, &PL_sv_undef);
206    
207     sv_magicext ((SV *)obj->self, 0, PERL_MAGIC_ext, &attachable::vtbl, (char *)obj, 0);
208     }
209    
210     obj->reattach ();
211     }
212    
213     object_thawer::~object_thawer ()
214     {
215     if (text) SvREFCNT_dec (text);
216     if (av) SvREFCNT_dec (av);
217 root 1.4
218     free ((void *)name);
219 root 1.1 }
220    
221 root 1.8 //TODO: remove
222 root 1.4 char *
223     fgets (char *s, int n, object_thawer &thawer)
224 root 1.1 {
225     char *p = thawer.line;
226     char *q = s;
227    
228     if (!p)
229     return 0;
230    
231     while (--n)
232     {
233     if (!*p)
234     break;
235    
236     *q++ = *p;
237    
238     if (*p++ == '\n')
239 root 1.8 {
240     ++thawer.linenum;
241     break;
242     }
243 root 1.1 }
244    
245     *q = 0;
246     thawer.line = p;
247    
248     return s == q ? 0 : s;
249     }
250    
251 root 1.8 bool
252     object_thawer::parse_error (keyword kw, const char *type, const char *name, bool skip)
253     {
254     if (!type) type = "file section";
255     if (!name) name = "generic";
256    
257     switch (kw)
258     {
259     case KW_EOF:
260     LOG (llevError, "%s:%d end of file while reading %s '%s', aborting load.\n",
261     this->name, linenum, type, name);
262     return false;
263    
264     case KW_ERROR:
265     LOG (llevError, "%s:%d errornous line (%s) while reading %s '%s', aborting load.\n",
266     this->name, linenum,
267     last_keyword ? last_keyword : "<null>",
268     type, name);
269     return false;
270    
271     default:
272     LOG (llevError, "%s:%d unexpected line (%s) while reading %s '%s', %s.\n",
273     this->name, linenum,
274     last_keyword ? last_keyword : "<null>",
275     last_value ? last_value : "<null>",
276     type, name,
277     skip ? "skipping line" : "aborting load");
278     return skip;
279     }
280     }
281    
282 root 1.4 keyword
283     object_thawer::get_kv ()
284 root 1.1 {
285     if (!line)
286     return KW_EOF;
287    
288     for (;;)
289     {
290     char *p = line;
291    
292     if (!*p)
293     return KW_EOF;
294    
295     // parse keyword
296     while (*p > ' ')
297     p++;
298    
299     int klen = p - line;
300    
301     if (*p++ != '\n')
302     {
303     // parse value
304     while (*(unsigned char *)p <= ' ' && *p != '\n') // skip 0x01 .. 0x20
305     ++p;
306    
307     last_value = p;
308    
309     while (*p != '\n')
310     p++;
311    
312     *p++ = 0;
313     }
314     else
315     last_value = 0;
316    
317 root 1.8 ++linenum;
318 root 1.1 line [klen] = 0;
319     keyword_idx *kw = kw_lex::match (line, klen);
320    
321     //printf ("KV %d<%s,%s>\n", kw ? kw->index : 0, line, last_value);//D
322    
323     last_keyword = line;
324     line = p;
325    
326     if (kw)
327     return kw->index;
328     else if (!*last_keyword || *last_keyword == '#')
329     ; // empty/comment line
330     else
331     return KW_ERROR;
332     }
333     }
334    
335 root 1.4 void
336     object_thawer::skip_kv (keyword kw)
337 root 1.1 {
338     shstr ml;
339    
340     switch (kw)
341     {
342     case KW_msg: get_ml (KW_endmsg , ml); break;
343     case KW_lore: get_ml (KW_endlore , ml); break;
344     case KW_maplore: get_ml (KW_endmaplore, ml); break;
345     }
346     }
347    
348 root 1.4 void
349     object_thawer::get (shstr &sh) const
350 root 1.1 {
351     if (last_value)
352     sh = last_value;
353     else
354     {
355     sh = "<value missing>";
356 root 1.5 LOG (llevError, "keyword \"%s\" requires value, substituting with <value missing>\n", last_keyword);//TODO: add filename
357 root 1.1 }
358     }
359    
360 root 1.4 void
361     object_thawer::get_ml (keyword kend, shstr &sh)
362 root 1.1 {
363     char kw[128];
364    
365     int klen = keyword_len [kend];
366    
367     kw [0] = '\n';
368     memcpy (kw + 1, keyword_str [kend], klen);
369     kw [klen + 1] = '\n';
370     kw [klen + 2] = 0;
371    
372 root 1.8 ++linenum;
373    
374 root 1.1 // first test for completely empty msg... "endXXX\n"
375     if (!strncmp (line, kw + 1, klen + 1))
376     {
377     sh = 0;
378    
379     line += klen + 1;
380    
381     return;
382     }
383     else
384     {
385     // multi-line strings are delimited by "\nendXXX\n" or "endXXX\n" (NULL)
386    
387     char *end = strstr (line, kw);
388    
389     if (!end)
390     {
391     sh = 0;
392     return;
393     }
394    
395     *end = 0;
396     sh = line;
397    
398 root 1.8 // count line numbers
399     while (line < end)
400     linenum += *line++ == '\n';
401    
402     line += keyword_len [kend];
403 root 1.1
404     while (*line++ != '\n')
405     ;
406 root 1.8
407     ++linenum;
408 root 1.1 }
409     }
410    
411 root 1.4 sint32
412     object_thawer::get_sint32 () const
413 root 1.1 {
414     char *p = last_value;
415    
416     if (!p)
417     return 0;
418    
419     sint32 val = 0;
420     bool negate;
421    
422     if (*p == '-')
423     {
424     negate = true;
425     ++p;
426     }
427     else
428     negate = false;
429    
430     do
431     {
432     val *= 10;
433     val += *p++ - '0';
434     }
435     while (*p);
436    
437     return negate ? -val : val;
438     }
439    
440 root 1.4 sint64
441     object_thawer::get_sint64 () const
442 root 1.1 {
443     return last_value ? atoll (last_value) : 0;
444     }
445    
446 root 1.4 double
447     object_thawer::get_double () const
448 root 1.1 {
449     return last_value ? atof (last_value) : 0;
450     }
451