1 /*
2  *
3  * Copyright (C) 1997 Eric A. Howe
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  *   Authors:  Eric A. Howe (mu@trends.net)
20  *             Bryan Henderson, 2010
21  */
22 #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
23 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
24 #define _BSD_SOURCE   /* Ensure strdup() is in <string.h> */
25 #include <assert.h>
26 #include <time.h>
27 #include <string.h>
28 
29 #include "mallocvar.h"
30 #include "nstring.h"
31 #include "ipdb.h"
32 
33 typedef uint32_t pilot_time_t;
34 
35 
36 
37 
38 static unsigned int
imgPpb(IMAGE * const imgP)39 imgPpb(IMAGE * const imgP) {
40 /*----------------------------------------------------------------------------
41    Pixels per byte
42 -----------------------------------------------------------------------------*/
43     return
44         imgP->type == IMG_GRAY   ? 4 :
45         imgP->type == IMG_GRAY16 ? 2 :
46         8;
47 }
48 
49 
50 
51 unsigned int
ipdb_img_ppb(IMAGE * const imgP)52 ipdb_img_ppb(IMAGE * const imgP) {
53 /*----------------------------------------------------------------------------
54    Pixels per byte
55 -----------------------------------------------------------------------------*/
56     return imgPpb(imgP);
57 }
58 
59 
60 
61 size_t
ipdb_img_size(IMAGE * const imgP)62 ipdb_img_size(IMAGE * const imgP) {
63 /*----------------------------------------------------------------------------
64   Size (in bytes) of an image's data.
65 -----------------------------------------------------------------------------*/
66     return (size_t)(imgP->width / imgPpb(imgP) * imgP->height);
67 }
68 
69 
70 
71 /*
72  * Return the start of row `r'.
73  */
74  uint8_t *
ipdb_img_row(IMAGE * const imgP,unsigned int const row)75  ipdb_img_row(IMAGE *      const imgP,
76               unsigned int const row) {
77 
78      return &imgP->data[(row) * imgP->width / imgPpb(imgP)];
79  }
80 
81 
82 
83  #define img_row(i, r)
84 
85  static pilot_time_t const unixepoch = (66*365+17)*24*3600;
86      /* The unix epoch in Mac time (the Mac epoch is 00:00 UTC 1904.01.01).
87         The 17 is the number of leap years.
88      */
89 
90  static const char * const errorDesc[] = {
91      /* E_BADCOLORS      */
92      "Invalid palette, only {0x00, 0x55, 0xAA, 0xFF} allowed.",
93 
94      /* E_NOTIMAGE       */
95      "Not an image file.",
96 
97      /* E_IMAGETHERE     */
98      "Image record already present, logic error.",
99 
100      /* E_IMAGENOTTHERE  */
101      "Image record required before text record, logic error.",
102 
103      /* E_TEXTTHERE      */
104      "Text record already present, logic error.",
105 
106      /* E_NOTRECHDR      */
107      "Invalid record header encountered.",
108 
109      /* E_UNKNOWNRECHDR  */
110      "Unknown record header.",
111 
112      /* E_TOOBIGG        */
113      "Image too big, maximum size approx. 640*400 gray pixels.",
114 
115      /* E_TOOBIGM        */
116      "Image too big, maximum size approx. 640*800 monochrome pixels.",
117  };
118 
119 
120 
121  const char *
ipdb_err(int const e)122  ipdb_err(int const e) {
123 
124      if (e < 0)
125          return e >= E_LAST ? errorDesc[-e - 1] : "unknown error";
126      else
127          return strerror(e);
128  }
129 
130 
131 
132  static void
rechdr_free(RECHDR * const recP)133  rechdr_free(RECHDR * const recP) {
134 
135      if (recP) {
136          free(recP->extra);
137          free(recP);
138      }
139  }
140 
141 
142 
143  void
ipdb_image_free(IMAGE * const imgP)144  ipdb_image_free(IMAGE * const imgP) {
145 
146      if (imgP) {
147          rechdr_free(imgP->r);
148          free(imgP->data);
149          free(imgP);
150      }
151  }
152 
153 
154 
155  void
ipdb_text_free(TEXT * const textP)156  ipdb_text_free(TEXT * const textP) {
157 
158      if (textP) {
159          rechdr_free(textP->r);
160          free(textP->data);
161          free(textP);
162      }
163  }
164 
165 
166 
167  void
ipdb_pdbhead_free(PDBHEAD * const headP)168  ipdb_pdbhead_free(PDBHEAD * const headP) {
169 
170      free(headP);
171  }
172 
173 
174 
175  void
ipdb_clear(IPDB * const pdbP)176  ipdb_clear(IPDB * const pdbP) {
177 
178      if (pdbP) {
179          ipdb_image_free(pdbP->i);
180          ipdb_text_free(pdbP->t);
181          ipdb_pdbhead_free(pdbP->p);
182     }
183 }
184 
185 
186 
187 void
ipdb_free(IPDB * const pdbP)188 ipdb_free(IPDB * const pdbP) {
189 
190     ipdb_clear(pdbP);
191     free(pdbP);
192 }
193 
194 
195 
196 PDBHEAD *
ipdb_pdbhead_alloc(const char * const name)197 ipdb_pdbhead_alloc(const char * const name) {
198 
199     PDBHEAD * pdbHeadP;
200 
201     MALLOCVAR(pdbHeadP);
202 
203     if (pdbHeadP) {
204         MEMSZERO(pdbHeadP);
205 
206         STRSCPY(pdbHeadP->name, name == NULL ? "unnamed" : name);
207 
208         /*
209          * All of the Image Viewer pdb files that I've come across have
210          * 3510939142U (1997.08.16 14:38:22 UTC) here.  I don't know where
211          * this bizarre date comes from but the real date works fine so
212          * I'm using it.
213          */
214         pdbHeadP->ctime =
215             pdbHeadP->mtime = (pilot_time_t)time(NULL) + unixepoch;
216 
217         MEMSCPY(&pdbHeadP->type, IPDB_vIMG);
218         MEMSCPY(&pdbHeadP->id,   IPDB_View);
219     }
220     return pdbHeadP;
221 }
222 
223 
224 
225 static RECHDR *
rechdr_alloc(int const type,uint32_t const offset)226 rechdr_alloc(int      const type,
227              uint32_t const offset) {
228 
229     /*
230      * We never produce the `extra' bytes (we only read them from a file)
231      * so there is no point allocating them here.
232      */
233 
234     RECHDR  * recHdrP;
235 
236     MALLOCVAR(recHdrP);
237 
238     if (recHdrP) {
239         MEMSSET(recHdrP, 0);
240 
241         recHdrP->offset   = offset;
242         recHdrP->rec_type = (uint8_t)(0xff & type);
243         MEMSCPY(&recHdrP->unknown, IPDB_MYST);
244     }
245     return recHdrP;
246 }
247 
248 
249 
250 /*
251  * The offset will be patched up as needed elsewhere.
252  */
253 #define IMGOFFSET   (PDBHEAD_SIZE + 8)
254 
255 
256 
257 IMAGE *
ipdb_image_alloc(const char * const name,int const type,int const w,int const h)258 ipdb_image_alloc(const char * const name,
259             int          const type,
260             int          const w,
261             int          const h) {
262 
263     bool failed;
264     IMAGE * imgP;
265 
266     failed = false;
267 
268     MALLOCVAR(imgP);
269 
270     if (imgP) {
271         MEMSZERO(imgP);
272 
273         STRSCPY(imgP->name, name == NULL ? "unnamed" : name);
274         imgP->type     = type;
275         imgP->x_anchor = 0xffff;
276         imgP->y_anchor = 0xffff;
277         imgP->width    = w;
278         imgP->height   = h;
279 
280         imgP->r = rechdr_alloc(IMG_REC, IMGOFFSET);
281 
282         if (imgP->r) {
283             if (w != 0 && h != 0) {
284                 MALLOCARRAY(imgP->data, w * h);
285 
286                 if (imgP->data != NULL) {
287                   memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h);
288                 } else
289                     failed = true;
290             }
291             if (failed)
292                 rechdr_free(imgP->r);
293         } else
294             failed = true;
295 
296         if (failed)
297             ipdb_image_free(imgP);
298     } else
299         failed = true;
300 
301     return failed ? NULL : imgP;
302 }
303 
304 
305 
306 TEXT *
ipdb_text_alloc(const char * const content)307 ipdb_text_alloc(const char * const content) {
308 
309     TEXT * textP;
310     bool failed;
311 
312     failed = false;
313     /*
314      * The offset will be patched up later on when we know what it
315      * should be.
316      */
317 
318     MALLOCVAR(textP);
319 
320     if (textP) {
321         MEMSZERO(textP);
322 
323         textP->r = rechdr_alloc(TEXT_REC, 0);
324 
325         if (textP->r) {
326             if (content) {
327                 textP->data = strdup(content);
328 
329                 if (!textP->data)
330                     failed = true;
331             }
332             if (failed)
333                 rechdr_free(textP->r);
334         } else
335             failed = true;
336 
337         if (failed)
338             ipdb_text_free(textP);
339     } else
340         failed = true;
341 
342     return failed ? NULL : textP;
343 }
344 
345 
346 
347 IPDB *
ipdb_alloc(const char * const name)348 ipdb_alloc(const char * const name) {
349 
350     IPDB * pdbP;
351     bool failed;
352 
353     failed = false;
354 
355     MALLOCVAR(pdbP);
356 
357     if (pdbP) {
358         MEMSZERO(pdbP);
359 
360         if (name) {
361             pdbP->p = ipdb_pdbhead_alloc(name);
362 
363             if (!pdbP->p)
364                 failed = true;
365         }
366         if (failed)
367             ipdb_free(pdbP);
368     } else
369         failed = true;
370 
371     return failed ? NULL : pdbP;
372 }
373 
374 
375 
376 const char *
ipdb_typeName(uint8_t const type)377 ipdb_typeName(uint8_t const type) {
378 
379     switch (type) {
380     case IMG_GRAY16: return "16 Bit Grayscale"; break;
381     case IMG_GRAY: return "Grayscale"; break;
382     case IMG_MONO: return "Monochrome"; break;
383     default: return "???";
384     }
385 }
386