1 /*
2     parse_simple.c - Part of psiconv, a PSION 5 file formats converter
3     Copyright (c) 1999-2014  Frodo Looijaard <frodo@frodo.looijaard.name>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 
20 #include "config.h"
21 #include "compat.h"
22 
23 #include <stdlib.h>
24 #include <math.h>
25 
26 #include "parse_routines.h"
27 #include "error.h"
28 #include "unicode.h"
29 
30 #ifdef DMALLOC
31 #include <dmalloc.h>
32 #endif
33 
34 static psiconv_float_t pow2(int n);
35 static psiconv_string_t psiconv_read_string_aux(const psiconv_config config,
36                                      const psiconv_buffer buf,int lev,
37                                      psiconv_u32 off,int *length, int *status,
38 				     int kind);
39 
40 /* Very inefficient, but good enough for now. By implementing it ourselves,
41    we do not have to link with -lm */
pow2(int n)42 psiconv_float_t pow2(int n)
43 {
44   psiconv_float_t res=1.0;
45   int i;
46 
47   for (i = 0; i < (n<0?-n:n); i++)
48     res *= 2.0;
49 
50   return n<0?1/res:res;
51 }
psiconv_read_u8(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * status)52 psiconv_u8 psiconv_read_u8(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,
53                            int *status)
54 {
55   psiconv_u8 *ptr;
56   ptr = psiconv_buffer_get(buf,off);
57   if (!ptr) {
58     psiconv_error(config,lev,off,"Trying byte read past the end of the file");
59     if (status)
60       *status = -PSICONV_E_PARSE;
61     return 0;
62   }
63   if (status)
64     *status = 0;
65   return *ptr;
66 }
67 
psiconv_read_u16(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * status)68 psiconv_u16 psiconv_read_u16(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,
69                              int *status)
70 {
71   psiconv_u8 *ptr0,*ptr1;
72   ptr0 = psiconv_buffer_get(buf,off);
73   ptr1 = psiconv_buffer_get(buf,off+1);
74   if (!ptr0 || !ptr1) {
75     psiconv_error(config,lev,off,"Trying word read past the end of the file");
76     if (status)
77       *status = -PSICONV_E_PARSE;
78     return 0;
79   }
80   if (status)
81     *status = 0;
82   return *ptr0 + (*ptr1 << 8);
83 }
84 
psiconv_read_u32(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * status)85 psiconv_u32 psiconv_read_u32(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,
86                              int *status)
87 {
88   psiconv_u8 *ptr0,*ptr1,*ptr2,*ptr3;
89   ptr0 = psiconv_buffer_get(buf,off);
90   ptr1 = psiconv_buffer_get(buf,off+1);
91   ptr2 = psiconv_buffer_get(buf,off+2);
92   ptr3 = psiconv_buffer_get(buf,off+3);
93   if (!ptr0 || !ptr1 || !ptr2 || !ptr3) {
94     psiconv_error(config,lev,off,"Trying long read past the end of the file");
95     if (status)
96       *status = -PSICONV_E_PARSE;
97     return 0;
98   }
99   if (status)
100     *status = 0;
101   return *ptr0 + (*ptr1 << 8) + (*ptr2 << 16) + (*ptr3 << 24);
102 }
103 
psiconv_read_sint(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,int * status)104 psiconv_s32 psiconv_read_sint(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,
105                               int *length,int *status)
106 {
107   int localstatus;
108   psiconv_u32 temp;
109 
110   temp=psiconv_read_u32(config,buf,lev,off,&localstatus);
111   if (status)
112     *status = localstatus;
113   if (length)
114     *length = localstatus?0:4;
115 
116   return localstatus?0:(temp & 0x7fffffff)*(temp&0x80000000?-1:1);
117 }
118 
psiconv_read_S(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,int * status)119 psiconv_u32 psiconv_read_S(const psiconv_config config,const psiconv_buffer buf, int lev, psiconv_u32 off,
120                            int *length,int *status)
121 {
122   psiconv_u8 temp;
123   psiconv_u32 res;
124   int len,localstatus;
125 
126   psiconv_progress(config,lev+1,off,"Going to read a S length indicator");
127   temp = psiconv_read_u8(config,buf,lev+2,off,&localstatus);
128   if (localstatus)
129     goto ERROR;
130   if ((temp & 0x03) == 0x02) {
131     res = psiconv_read_u8(config,buf,lev+2,off,&localstatus) >> 2;
132     if (localstatus)
133       goto ERROR;
134     len = 1;
135     psiconv_debug(config,lev+2,off,"Indicator (1 byte): %02x",res);
136   } else if ((temp & 0x07) == 0x05) {
137     res = psiconv_read_u16(config,buf,lev+2,off,&localstatus) >> 3;
138     if (localstatus)
139       goto ERROR;
140     len = 2;
141     psiconv_debug(config,lev+2,off,"Indicator (2 bytes): %04x",res);
142   } else {
143     psiconv_error(config,lev+2,off,"S indicator: unknown encoding!");
144     psiconv_debug(config,lev+2,off,"Raw data first byte: %02x",temp);
145     goto ERROR;
146   }
147 
148   if (length)
149     *length = len;
150   if (status)
151     *status = 0;
152 
153   psiconv_progress(config,lev+1,off+len-1,
154                    "End of S length indicator (total length: %08x)", len);
155 
156   return res;
157 
158 ERROR:
159   psiconv_error(config,lev+1,off,"Reading of S indicator failed");
160   if (status)
161     *status = localstatus;
162   if (length)
163      *length = 0;
164   return 0;
165 }
166 
psiconv_read_X(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,int * status)167 psiconv_u32 psiconv_read_X(const psiconv_config config,const psiconv_buffer buf, int lev, psiconv_u32 off,
168                            int *length, int *status)
169 {
170   psiconv_u8 temp;
171   psiconv_u32 res;
172   int len,localstatus;
173 
174   psiconv_progress(config,lev+1,off,"Going to read a X length indicator");
175   temp = psiconv_read_u8(config,buf,lev+2,off,&localstatus);
176   if (localstatus)
177     goto ERROR;
178   if ((temp & 0x01) == 0x00) {
179     res = psiconv_read_u8(config,buf,lev+2,off,&localstatus) >> 1;
180     if (localstatus)
181       goto ERROR;
182     len = 1;
183     psiconv_debug(config,lev+2,off,"Indicator (1 byte): %02x",res);
184   } else if ((temp & 0x03) == 0x01) {
185     res = psiconv_read_u16(config,buf,lev+2,off,&localstatus) >> 2;
186     if (localstatus)
187       goto ERROR;
188     len = 2;
189     psiconv_debug(config,lev+2,off,"Indicator (2 bytes): %04x",res);
190   } else if ((temp & 0x07) == 0x03) {
191     res = psiconv_read_u32(config,buf,lev+2,off,&localstatus) >> 3;
192     if (localstatus)
193       goto ERROR;
194     len = 4;
195     psiconv_debug(config,lev+2,off,"Indicator (4 bytes): %08x",res);
196   } else {
197     psiconv_error(config,lev+2,off,"X indicator: unknown encoding!");
198     psiconv_debug(config,lev+2,off,"Raw data first byte: %02x",temp);
199     goto ERROR;
200   }
201 
202   if (length)
203     *length = len;
204   if (status)
205     *status = 0;
206 
207   psiconv_progress(config,lev+1,off+len-1,
208                    "End of X length indicator (total length: %08x)", len);
209 
210   return res;
211 
212 ERROR:
213   psiconv_error(config,lev+1,off,"Reading of X indicator failed");
214   if (status)
215     *status = localstatus;
216   if (length)
217      *length = 0;
218   return 0;
219 }
220 
psiconv_read_length(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,int * status)221 psiconv_length_t psiconv_read_length(const psiconv_config config,const psiconv_buffer buf, int lev,
222                                      psiconv_u32 off, int *length, int *status)
223 {
224   psiconv_length_t res;
225   int localstatus;
226 
227   res = (2.54/1440.0) * ((psiconv_s32) psiconv_read_u32(config,buf,lev,off,
228                                                         &localstatus));
229   if (localstatus) {
230     psiconv_error(config,lev+1,off,"Reading of length failed");
231     if (length)
232       *length = 0;
233     if (status)
234        *status = localstatus;
235      return 0;
236   }
237   psiconv_debug(config,lev+1,off,"Length: %f",res);
238   if (length)
239     *length = 4;
240   if (status)
241      *status = 0;
242   return res;
243 }
244 
psiconv_read_size(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,int * status)245 psiconv_size_t psiconv_read_size(const psiconv_config config,const psiconv_buffer buf, int lev,
246                                  psiconv_u32 off, int *length, int *status)
247 {
248   psiconv_size_t res;
249   int localstatus;
250   res = ((psiconv_s32) psiconv_read_u32(config,buf,lev,off,&localstatus)) / 20.0;
251   if (localstatus) {
252     psiconv_error(config,lev+1,off,"Reading of size failed");
253     if (length)
254       *length = 0;
255     if (status)
256        *status = localstatus;
257      return 0;
258   }
259   psiconv_debug(config,lev+1,off,"Size: %f",res);
260   if (status)
261      *status = 0;
262   if (length)
263     *length = 4;
264   return res;
265 }
266 
psiconv_parse_bool(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,psiconv_bool_t * result)267 int psiconv_parse_bool(const psiconv_config config,const psiconv_buffer buf, int lev, psiconv_u32 off,
268                        int *length, psiconv_bool_t *result)
269 {
270   psiconv_u8 temp;
271   int localstatus;
272   temp = psiconv_read_u8(config,buf,lev,off,&localstatus);
273   if (localstatus) {
274     psiconv_error(config,lev+1,off,"Reading of bool failed");
275     if (length)
276       *length = 0;
277      return localstatus;
278   }
279   if (length)
280     *length = 1;
281   if (temp == 0) {
282     *result = psiconv_bool_false;
283     return 0;
284   } else if (temp == 1) {
285     *result = psiconv_bool_true;
286     return 0;
287   }
288   psiconv_warn(config,lev+1,off,"Unknown value for boolean");
289   psiconv_debug(config,lev+1,off,"Boolean value: %02x",temp);
290   *result = psiconv_bool_true;
291   return 0;
292 }
293 
psiconv_read_string(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,int * status)294 psiconv_string_t psiconv_read_string(const psiconv_config config,
295                                      const psiconv_buffer buf,int lev,
296                                      psiconv_u32 off,int *length, int *status)
297 {
298   return psiconv_read_string_aux(config,buf,lev,off,length,status,-1);
299 }
300 
psiconv_read_short_string(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,int * status)301 psiconv_string_t psiconv_read_short_string(const psiconv_config config,
302                                      const psiconv_buffer buf,int lev,
303                                      psiconv_u32 off,int *length, int *status)
304 {
305   return psiconv_read_string_aux(config,buf,lev,off,length,status,-2);
306 }
307 
psiconv_read_charlist(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int nrofchars,int * status)308 psiconv_string_t psiconv_read_charlist(const psiconv_config config,
309                                        const psiconv_buffer buf, int lev,
310 				       psiconv_u32 off, int nrofchars,
311 				       int *status)
312 {
313   int length;
314   if (nrofchars <= 0) {
315     psiconv_error(config,lev,off,
316 	          "psiconv_read_charlist called with non-positive nrofchars");
317     if (status)
318       *status = -PSICONV_E_OTHER;
319     return NULL;
320   }
321   return psiconv_read_string_aux(config,buf,lev,off,&length,status,nrofchars);
322 }
323 
324 
psiconv_read_string_aux(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,int * status,int kind)325 psiconv_string_t psiconv_read_string_aux(const psiconv_config config,
326                                      const psiconv_buffer buf,int lev,
327                                      psiconv_u32 off,int *length, int *status,
328 				     int kind)
329 {
330   int bytecount,i,leng,len,localstatus;
331   psiconv_string_t result;
332   char *res_copy;
333   psiconv_list string;
334   psiconv_ucs2 nextchar;
335   psiconv_ucs2 *nextcharptr;
336 
337   psiconv_progress(config,lev+1,off,"Going to read a string");
338 
339   if (kind == -1)
340     bytecount = psiconv_read_S(config,buf,lev+2,off,&leng,&localstatus);
341   else if (kind == -2) {
342     bytecount = psiconv_read_u8(config,buf,lev+2,off,&localstatus);
343     leng = 1;
344   } else {
345     bytecount = kind;
346     leng = 0;
347     localstatus = 0;
348   }
349   if (localstatus)
350     goto ERROR1;
351   psiconv_debug(config,lev+2,off,"Length: %i",bytecount);
352   len = leng;
353 
354   if (!(string = psiconv_list_new(sizeof(*result))))
355     goto ERROR1;
356 
357   /* Read the string into a temporary list */
358   i = 0;
359   while (i < bytecount) {
360     nextchar = psiconv_unicode_read_char(config,buf,lev,off+i+len,
361 	                                  &leng,&localstatus);
362     if (localstatus)
363       goto ERROR2;
364     if ((localstatus = psiconv_list_add(string,&nextchar)))
365       goto ERROR2;
366     i += leng;
367   }
368   if (i > bytecount) {
369     psiconv_error(config,lev,off+i+len,"Malformed string");
370     localstatus = PSICONV_E_PARSE;
371     goto ERROR2;
372   }
373   len += bytecount;
374 
375   /* Copy the list to the actual string */
376   if (!(result = malloc(sizeof(*result) * (psiconv_list_length(string) + 1))))
377     goto ERROR2;
378   for (i = 0; i < psiconv_list_length(string); i++) {
379     if (!(nextcharptr = psiconv_list_get(string,i))) {
380       psiconv_error(config,lev,off+i+len,"Data structure corruption");
381       goto ERROR3;
382     }
383     result[i] = *nextcharptr;
384   }
385   result[i] = 0;
386 
387   res_copy = psiconv_make_printable(config,result);
388   if (!res_copy)
389     goto ERROR3;
390   psiconv_debug(config,lev+2,off,"Contents: `%s'",res_copy);
391   free(res_copy);
392 
393   psiconv_list_free(string);
394 
395   if (length)
396     *length = len;
397 
398   if (status)
399     *status = 0;
400 
401   psiconv_progress(config,lev+1,off+len-1,"End of string (total length: %08x)",len);
402 
403   return result;
404 
405 ERROR3:
406   free(result);
407 ERROR2:
408   psiconv_list_free(string);
409 ERROR1:
410   psiconv_error(config,lev+1,off,"Reading of string failed");
411   if (status)
412     *status = localstatus;
413   if (length)
414     *length = 0;
415   return NULL;
416 }
417 
psiconv_read_float(const psiconv_config config,const psiconv_buffer buf,int lev,psiconv_u32 off,int * length,int * status)418 psiconv_float_t psiconv_read_float(const psiconv_config config,const psiconv_buffer buf, int lev,
419                                    psiconv_u32 off, int *length, int *status)
420 {
421   psiconv_float_t result,bitvalue;
422   int res,bit;
423   psiconv_u32 temp=0;
424 
425   psiconv_progress(config,lev+1,off,"Going to read a float");
426 
427   bitvalue = 0.5;
428   result = 1.0;
429   for (bit = 0x33; bit > 0; bit--) {
430     if ((bit == 0x33) || ((bit & 0x07) == 0x07))
431       temp = psiconv_read_u8(config,buf,lev+2,off+ (bit >> 3),&res);
432     if (res)
433       goto ERROR;
434     if (temp & (0x01 << (bit & 0x07)))
435       result += bitvalue;
436     bitvalue /= 2.0;
437   }
438   temp = psiconv_read_u16(config,buf,lev+2,off+6,&res);
439   if (res)
440     goto ERROR;
441   if (temp & 0x8000)
442     result = -result;
443   temp = (temp & 0x7ff0) >> 4;
444   result *= pow2(((int) temp)-0x3ff);
445   psiconv_debug(config,lev+1,off,"Float value: %f",result);
446   if (length)
447     *length = 8;
448   if (*status)
449     *status = res;
450   return result;
451 ERROR:
452   psiconv_error(config,lev+1,off,"Reading of float failed");
453   if (length)
454     *length = 0;
455   if (*status)
456     *status = res;
457   return 0.0;
458 }
459 
460