]> git.tdb.fi Git - libs/datafile.git/blob - source/binaryparser.cpp
Fix EOF handling
[libs/datafile.git] / source / binaryparser.cpp
1 /* $Id$
2
3 This file is part of libmspdatafile
4 Copyright © 2007  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <sys/param.h>
9 #include <msp/strings/formatter.h>
10 #include "binaryparser.h"
11 #include "input.h"
12
13 using namespace std;
14
15 namespace Msp {
16 namespace DataFile {
17
18 BinaryParser::BinaryParser(Input &i, const string &s):
19         ParserMode(i, s),
20         first(true)
21 {
22         dict[1]=DictEntry("__kwd", "iss");
23         dict[2]=DictEntry("__str", "is");
24 }
25
26 Statement BinaryParser::parse()
27 {
28         while(1)
29         {
30                 Statement st=parse_statement();
31                 if(st.keyword=="__kwd")
32                 {
33                         if(st.args.size()!=3)
34                                 throw TypeError(src+": Keyword definition must have three arguments");
35
36                         const unsigned id=st.args[0].get<unsigned>();
37                         const string &kw=st.args[1].get<const string &>();
38                         const string &args=st.args[2].get<const string &>();
39                         dict[id]=DictEntry(kw, args);
40                 }
41                 else if(st.keyword=="__str")
42                 {
43                         if(st.args.size()!=2)
44                                 throw TypeError(src+": String definition must have two arguments");
45
46                         const unsigned id=st.args[0].get<unsigned>();
47                         strings[id]=st.args[1].get<const string &>();
48                 }
49                 else
50                         return st;
51         }
52 }
53
54 Statement BinaryParser::parse_statement()
55 {
56         while(first && in.peek()=='\n')
57                 in.get();
58         first=false;
59
60         unsigned id=parse_int();
61         if(!in)
62                 return Statement();
63
64         Dictionary::const_iterator i=dict.find(id);
65         if(i==dict.end())
66                 throw ParseError(format("%s: Unknown statement ID %d", src, id), src, 0);
67         const DictEntry &de=i->second;
68
69         Statement result;
70         result.keyword=de.keyword;
71         result.source=src;
72
73         for(unsigned j=0; j<de.args.size(); ++j)
74         {
75                 switch(de.args[j])
76                 {
77                 case 'i':
78                         result.args.push_back(parse_int());
79                         break;
80                 case 'f':
81                         result.args.push_back(parse_float());
82                         break;
83                 case 's':
84                         result.args.push_back(parse_string());
85                         break;
86                 case 'b':
87                         result.args.push_back(parse_bool());
88                         break;
89                 case 'e':
90                         result.args.push_back(Value(ENUM, parse_enum()));
91                         break;
92                 }
93         }
94
95         unsigned nsub=parse_int();
96         for(unsigned j=0; j<nsub; ++j)
97                 result.sub.push_back(parse());
98
99         result.valid=true;
100
101         return result;
102 }
103
104 long long BinaryParser::parse_int()
105 {
106         long long result=0;
107         unsigned bits=0;
108
109         while(in)
110         {
111                 int c=in.get();
112
113                 result=(result<<7) | (c&0x7F);
114                 bits+=7;
115
116                 if(!(c&0x80))
117                         break;
118         }
119
120         const long long mask=1LL<<(bits-1);
121         result=(result^mask)-mask;
122
123         return result;
124 }
125
126 float BinaryParser::parse_float()
127 {
128         union
129         {
130                 float f;
131                 char d[sizeof(float)];
132         };
133
134 #if BYTE_ORDER == LITTLE_ENDIAN
135         for(unsigned i=sizeof(float); i--;)
136                 d[i]=in.get();
137 #else
138         for(unsigned i=0; i<sizeof(float); ++i)
139                 d[i]=in.get();
140 #endif
141
142         return f;
143 }
144
145 bool BinaryParser::parse_bool()
146 {
147         return in.get();
148 }
149
150 string BinaryParser::parse_string()
151 {
152         int len=parse_int();
153         if(len>=0)
154         {
155                 string result;
156                 result.reserve(len);
157                 for(int i=0; i<len; ++i)
158                         result+=in.get();
159                 return result;
160         }
161         else
162                 return lookup_string(-len);
163 }
164
165 string BinaryParser::parse_enum()
166 {
167         return lookup_string(parse_int());
168 }
169
170 const string &BinaryParser::lookup_string(unsigned id) const
171 {
172         StringMap::const_iterator i=strings.find(id);
173         if(i==strings.end())
174                 throw KeyError("Unknown string", lexical_cast(id));
175         return i->second;
176 }
177
178 } // namespace DataFile
179 } // namespace Msp