1 /*
2 * Copyright 2012 Peter Curtis
3 *
4 * This file is part of libocad.
5 *
6 * libocad is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * libocad is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with libocad. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <fcntl.h>
21 #include <stddef.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #ifndef _MSC_VER
27 #include <unistd.h>
28 #endif
29
30 #include "libocad.h"
31
32 /** Structure for compacting entity indexes - used in the compaction callback functions.
33 */
34 typedef struct _IndexBuilder {
35 u8 *p; // Pointer where we can start writing data
36 u8 *base; // Pointer to base of memory block (base - p is used for offsets)
37 dword *pfirst; // Pointer to location of first index block offset
38 void *idx; // Pointer to the current index block, starts out as NULL
39 int i; // Index of the last object written to the index block, starts out as 0
40 } IndexBuilder;
41
42 /** Copies src to dest, advances the destination pointer to a dword alignment, and returns
43 * the new value of the destination pointer.
44 */
copy_and_advance(u8 * dest,const void * src,u32 size)45 static u8 *copy_and_advance(u8 *dest, const void *src, u32 size) {
46 u64 tmp;
47 u8 *end;
48 if (src != NULL) memcpy(dest, src, size);
49 // align to dword
50 tmp = (u64)dest + size + (4 - 1);
51 tmp -= tmp % 4;
52 end = (u8 *)tmp;
53 if (src == NULL) size = 0;
54 memset(dest + size, 0, (end - (dest + size)));
55 return (u8 *)tmp;
56 }
57
ocad_file_compact_symbol_cb(void * param,OCADFile * pfile,OCADSymbol * symbol)58 static bool ocad_file_compact_symbol_cb(void *param, OCADFile *pfile, OCADSymbol *symbol) {
59 OCADSymbolEntry *entry;
60 IndexBuilder *b = (IndexBuilder *)param;
61 OCADSymbolIndex *idx = (OCADSymbolIndex *)b->idx;
62 if (++b->i == 256) {
63 u8 *next = b->p;
64 b->p = copy_and_advance(b->p, NULL, sizeof(OCADSymbolIndex));
65 if (idx) idx->next = (next - b->base); else *(b->pfirst) = (next - b->base);
66 b->idx = next; b->i = 0;
67 idx = (OCADSymbolIndex *)b->idx;
68 }
69 entry = &(idx->entry[b->i]);
70
71 entry->ptr = (b->p - b->base);
72
73 b->p = copy_and_advance(b->p, symbol, symbol->size);
74 return TRUE;
75 }
76
ocad_file_compact_object_entry_cb(void * param,OCADFile * pfile,OCADObjectEntry * entry)77 static bool ocad_file_compact_object_entry_cb(void *param, OCADFile *pfile, OCADObjectEntry *entry) {
78 OCADObjectEntry *enew;
79 OCADObject *object;
80 IndexBuilder *b = (IndexBuilder *)param;
81 OCADObjectIndex *idx = (OCADObjectIndex *)b->idx;
82 if (++b->i == 256) {
83 u8 *next = b->p;
84 b->p = copy_and_advance(b->p, NULL, sizeof(OCADObjectIndex));
85 if (idx) idx->next = (next - b->base); else *(b->pfirst) = (next - b->base);
86 b->idx = next; b->i = 0;
87 idx = (OCADObjectIndex *)b->idx;
88 }
89 enew = &(idx->entry[b->i]);
90 object = ocad_object(pfile, entry);
91
92 enew->ptr = (b->p - b->base);
93 enew->npts = object->npts + object->ntext;
94 ocad_object_entry_refresh(pfile, enew, object);
95
96 b->p = copy_and_advance(b->p, object, ocad_object_size(object));
97 return TRUE;
98 }
99
ocad_file_compact_string_entry_cb(void * param,OCADFile * pfile,OCADStringEntry * entry)100 static bool ocad_file_compact_string_entry_cb(void *param, OCADFile *pfile, OCADStringEntry *entry) {
101 IndexBuilder *b = (IndexBuilder *)param;
102 OCADStringIndex *idx = (OCADStringIndex *)b->idx;
103 OCADStringEntry *enew;
104 OCADCString *str;
105 if (++b->i == 256) {
106 u8 *next = b->p;
107 b->p = copy_and_advance(b->p, NULL, sizeof(OCADStringIndex));
108 if (idx) idx->next = (next - b->base); else *(b->pfirst) = (next - b->base);
109 b->idx = next; b->i = 0;
110 idx = (OCADStringIndex *)b->idx;
111 }
112 enew = &(idx->entry[b->i]);
113 str = ocad_string(pfile, entry);
114
115 // Write a new index entry
116 enew->ptr = (b->p - b->base);
117 enew->size = entry->size;
118 enew->type = entry->type;
119
120 b->p = copy_and_advance(b->p, str, entry->size);
121 return TRUE;
122 }
123
124
ocad_init()125 int ocad_init() {
126 // Do some assertions to make sure stuff is packed properly
127 if ( sizeof(OCADFileHeader) != 0x48
128 || sizeof(OCADColor) != 0x48
129 ){
130 fprintf(stderr, "*** WARNING: structure packing check failed. libocad was probably not compiled correctly.***\n");
131 // The "(long unsigned int)" fixes a MinGW C99 peculiarity on x86_64.
132 fprintf(stderr, "magic = %02lx\n", (long unsigned int)offsetof(OCADFileHeader, magic));
133 fprintf(stderr, "ftype = %02lx\n", (long unsigned int)offsetof(OCADFileHeader, ftype));
134 fprintf(stderr, "major = %02lx\n", (long unsigned int)offsetof(OCADFileHeader, major));
135 fprintf(stderr, "minor = %02lx\n", (long unsigned int)offsetof(OCADFileHeader, minor));
136 fprintf(stderr, "osymidx = %02lx\n", (long unsigned int)offsetof(OCADFileHeader, osymidx));
137 fprintf(stderr, "oobjidx = %02lx\n", (long unsigned int)offsetof(OCADFileHeader, oobjidx));
138 fprintf(stderr, "osetup = %02lx\n", (long unsigned int)offsetof(OCADFileHeader, osetup));
139 fprintf(stderr, "ssetup = %02lx\n", (long unsigned int)offsetof(OCADFileHeader, ssetup));
140 fprintf(stderr, "Sizeof char is %lu\n", (long unsigned int)sizeof(char));
141 fprintf(stderr, "Sizeof short is %lu\n", (long unsigned int)sizeof(short));
142 fprintf(stderr, "Sizeof int is %lu\n", (long unsigned int)sizeof(int));
143 fprintf(stderr, "Sizeof long is %lu\n", (long unsigned int)sizeof(long));
144 fprintf(stderr, "Sizeof long long is %lu\n", (long unsigned int)sizeof(long long));
145 exit(-10);
146 }
147 return 0;
148 }
149
ocad_shutdown()150 int ocad_shutdown() {
151 return 0;
152 }
153
ocad_file_open(OCADFile ** pfile,const char * filename)154 int ocad_file_open(OCADFile **pfile, const char *filename) {
155 struct stat fs;
156 int left;
157 int err = 0;
158 u8 *p;
159 dword offs;
160 OCADFile *file = *pfile;
161 if (file == NULL) {
162 file = (OCADFile *)malloc(sizeof(OCADFile));
163 if (file == NULL) return -1;
164 }
165 memset(file, 0, sizeof(OCADFile));
166 file->filename = (const char *)my_strdup(filename);
167 file->fd = open(file->filename, O_RDONLY | O_BINARY);
168 if (file->fd <= 0) { err = -2; goto ocad_file_open_1; }
169 if (fstat(file->fd, &fs) < 0) { err = -3; goto ocad_file_open_1; }
170 file->size = fs.st_size;
171 file->reserved_size = file->size;
172
173 #ifdef MMAP_AVAILABLE
174 file->buffer = mmap(NULL, file->size, PROT_READ | PROT_WRITE, 0, file->fd, 0);
175 if (file->buffer == NULL || file->buffer == MAP_FAILED) { err = -4; goto ocad_file_open_1; }
176 #else
177 file->mapped = FALSE;
178 file->buffer = malloc(file->size);
179 if (file->buffer == NULL) { err = -1; goto ocad_file_open_1; }
180 left = file->size;
181 p = file->buffer;
182 while (left > 0) {
183 int got = read(file->fd, p, left);
184 if (got <= 0) { fprintf(stderr,"got=%d sz=%d\n",got,file->size);err = -4; goto ocad_file_open_1; }
185 p += got; left -= got;
186 }
187 #endif
188
189 file->header = (OCADFileHeader *)file->buffer;
190 file->colors = (OCADColor *)(file->buffer + 0x48);
191 offs = file->header->osetup;
192 if (offs > 0) file->setup = (OCADSetup *)(file->buffer + offs);
193
194 *pfile = file;
195 goto ocad_file_open_0;
196
197 ocad_file_open_1:
198 ocad_file_close(file);
199 ocad_file_open_0:
200 return err;
201 }
202
ocad_file_open_mapped(OCADFile ** pfile,const char * filename)203 int ocad_file_open_mapped(OCADFile **pfile, const char *filename) {
204 return -10;
205 }
206
ocad_file_open_memory(OCADFile ** pfile,u8 * buffer,u32 size)207 int ocad_file_open_memory(OCADFile **pfile, u8* buffer, u32 size) {
208 dword offs;
209
210 OCADFile *file = *pfile;
211 if (file == NULL) {
212 file = (OCADFile *)malloc(sizeof(OCADFile));
213 if (file == NULL) return -1;
214 }
215 memset(file, 0, sizeof(OCADFile));
216
217 file->mapped = FALSE;
218 file->size = size;
219 file->buffer = buffer;
220 if (file->buffer == NULL) { return -1; }
221
222 file->header = (OCADFileHeader *)file->buffer;
223 file->colors = (OCADColor *)(file->buffer + 0x48);
224 offs = file->header->osetup;
225 if (offs > 0) file->setup = (OCADSetup *)(file->buffer + offs);
226
227 *pfile = file;
228 return 0;
229 }
230
ocad_file_close(OCADFile * pfile)231 int ocad_file_close(OCADFile *pfile) {
232 #ifdef MMAP_AVAILABLE
233 if (pfile->buffer) munmap(pfile->buffer, pfile->size);
234 #else
235 if (pfile->buffer) free(pfile->buffer);
236 #endif
237 if (pfile->fd) close(pfile->fd);
238 if (pfile->filename) free((void *)pfile->filename);
239 return 0;
240 }
241
ocad_file_save(OCADFile * pfile)242 int ocad_file_save(OCADFile *pfile) {
243 if (pfile->mapped) {
244 // FIXME: sync the memory map
245 return -10;
246 }
247 return ocad_file_save_as(pfile, pfile->filename);
248 }
249
ocad_file_save_as(OCADFile * pfile,const char * filename)250 int ocad_file_save_as(OCADFile *pfile, const char *filename) {
251 // This behaves the same whether or not the file is memory mapped
252 // It saves to another file, without modifying the filename
253 int err = 0;
254 int got;
255 int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0664);
256 if (fd < 0) { err = -2; goto ocad_file_save_as_0; }
257 got = write(fd, pfile->buffer, pfile->size);
258 if (got != pfile->size) { err = -3; goto ocad_file_save_as_1; }
259
260 ocad_file_save_as_1:
261 close(fd);
262 ocad_file_save_as_0:
263 return err;
264 }
265
ocad_file_new(OCADFile ** pfile)266 int ocad_file_new(OCADFile **pfile) {
267 OCADFile *pnew;
268 u8 *dest, *p;
269 u32 size;
270
271 // Create OCADFile struct
272 pnew = (OCADFile *)malloc(sizeof(OCADFile));
273 if (pnew == NULL) return -1;
274 pnew->filename = NULL;
275 pnew->fd = 0;
276 pnew->mapped = FALSE;
277
278 // Allocate buffer
279 size = 1024 * 1024; // start with 1 MiB
280 dest = (u8 *)malloc(size);
281 if (dest == NULL) return -1;
282 memset(dest, 0, size);
283 p = dest;
284
285 pnew->buffer = dest;
286 pnew->reserved_size = size;
287
288 // Place header at start
289 pnew->header = (OCADFileHeader *)dest;
290 p += sizeof(OCADFileHeader);
291
292 // Color table
293 pnew->colors = (OCADColor *)p;
294 p += 256 * sizeof(OCADColor);
295 p += 32 * sizeof(OCADColorSeparation);
296
297 // Setup data
298 pnew->header->osetup = (p - dest);
299 pnew->header->ssetup = sizeof(OCADSetup); // NOTE: This does not include all possible elements!
300 pnew->setup = (OCADSetup *)p;
301 p += pnew->header->ssetup;
302
303 // First symbol block
304 pnew->header->osymidx = (p - dest);
305 p += sizeof(OCADSymbolIndex);
306
307 // First object block
308 pnew->header->oobjidx = (p - dest);
309 p += sizeof(OCADObjectIndex);
310
311 // String block
312 pnew->header->ostringidx = (p - dest);
313 p += sizeof(OCADStringIndex);
314
315 // Done
316 pnew->size = (p - dest);
317 *pfile = pnew;
318 return 0;
319 }
320
ocad_file_reserve(OCADFile * file,int amount)321 int ocad_file_reserve(OCADFile *file, int amount) {
322 u32 old_reserved_size = file->reserved_size;
323 if (file->reserved_size - file->size >= amount)
324 return 0;
325
326 u32 header_offset = (u8*)file->header - file->buffer;
327 u32 colors_offset = (u8*)file->colors - file->buffer;
328 u32 setup_offset = (u8*)file->setup - file->buffer;
329
330 while (file->reserved_size - file->size < amount) {
331 file->reserved_size *= 2;
332 }
333 file->buffer = realloc(file->buffer, file->reserved_size);
334
335 file->header = (OCADFileHeader*)(file->buffer + header_offset);
336 file->colors = (OCADColor*)(file->buffer + colors_offset);
337 file->setup = (OCADSetup*)(file->buffer + setup_offset);
338
339 if (file->buffer == NULL) return -1;
340 memset(file->buffer + old_reserved_size, 0, file->reserved_size - old_reserved_size);
341 return 0;
342 }
343
ocad_file_compact(OCADFile * pfile)344 int ocad_file_compact(OCADFile *pfile) {
345 OCADFile dfile, *pnew;
346 u8 *dest, *p;
347 u32 size;
348 IndexBuilder b;
349 if (!pfile || !pfile->header) return -1; // invalid file
350
351 // compact should always make the file smaller, so the current size should be enough?
352 pnew = &dfile;
353 dest = (u8 *)malloc(pfile->size);
354 p = dest;
355 pnew->buffer = dest;
356 pnew->size = pfile->size; // will change this later...
357 pnew->reserved_size = pfile->size;
358 pnew->header = (OCADFileHeader *)dest;
359
360 b.base = dest;
361 fprintf(stderr, "Compacting: base=%p\n", dest);
362
363 // Copy the file header
364 p = copy_and_advance(p, pfile->header, sizeof(OCADFileHeader));
365 fprintf(stderr, "Compacting: copied file header, p=%p\n", p);
366
367 // Copy the color table
368 pnew->colors = (OCADColor *)p;
369 size = pfile->header->ncolors * sizeof(OCADColor);
370 p = copy_and_advance(p, pfile->colors, size);
371 fprintf(stderr, "Compacting: copied color table, p=%p\n", p);
372
373 // Copy the setup data
374 size = pfile->header->ssetup;
375 pnew->header->osetup = (p - dest);
376 pnew->header->ssetup = size;
377 pnew->setup = (OCADSetup *)p;
378 p = copy_and_advance(p, pfile->setup, size);
379 fprintf(stderr, "Compacting: copied setup block, p=%p\n", p);
380
381 // Copy the symbols
382 b.p = p; b.pfirst = &(pnew->header->osymidx); b.idx = NULL; b.i = 255;
383 ocad_symbol_iterate(pfile, ocad_file_compact_symbol_cb, &b);
384 fprintf(stderr, "Compacting: copied symbols, p=%p\n", b.p);
385 //dump_bytes(dest, b.p - dest);
386
387 // Copy the objects
388 b.pfirst = &(pnew->header->oobjidx); b.idx = NULL; b.i = 255;
389 ocad_object_entry_iterate(pfile, ocad_file_compact_object_entry_cb, &b);
390 fprintf(stderr, "Compacting: copied objects, p=%p\n", b.p);
391
392 // Copy the strings
393 b.pfirst = &(pnew->header->ostringidx); b.idx = NULL; b.i = 255;
394 ocad_string_entry_iterate(pfile, ocad_file_compact_string_entry_cb, &b);
395 fprintf(stderr, "Compacting: copied template, p=%p\n", b.p);
396
397 // We're done!
398 p = b.p;
399 pnew->size = (p - dest);
400 fprintf(stderr, "Compaction changed size from %x to %x\n", pfile->size, pnew->size);
401 free(pfile->buffer);
402 pfile->buffer = pnew->buffer;
403 pfile->size = pnew->size;
404 pfile->header = pnew->header;
405 pfile->colors = pnew->colors;
406 pfile->setup = pnew->setup;
407
408 return 0;
409 }
410
ocad_export(OCADFile * pfile,void * opts)411 int ocad_export(OCADFile *pfile, void *opts) {
412 OCADExportOptions *options = (OCADExportOptions *)opts;
413 return options->do_export(pfile, options);
414 }
415
ocad_export_file(OCADFile * pfile,const char * filename,void * opts)416 int ocad_export_file(OCADFile *pfile, const char *filename, void *opts) {
417 int ret;
418 OCADExportOptions *options = (OCADExportOptions *)opts;
419 FILE *file = fopen(filename, "wb");
420 if (file == NULL) return -2;
421 options->output = file;
422 ret = ocad_export(pfile, options);
423 fclose(file);
424 return ret;
425 }
426
427
428
429 typedef struct _ocad_file_bounds_data {
430 bool empty;
431 OCADRect rect;
432 } ocad_file_bounds_data;
433
ocad_file_bounds_union(ocad_file_bounds_data * d,const OCADRect * r)434 static void ocad_file_bounds_union(ocad_file_bounds_data *d, const OCADRect *r) {
435 if (d->empty) {
436 memcpy(&(d->rect), r, sizeof(OCADRect));
437 d->empty = FALSE;
438 }
439 else {
440 ocad_rect_union(&(d->rect), r);
441 }
442 }
443
ocad_file_bounds_cb(void * param,OCADFile * file,OCADObjectEntry * entry)444 static bool ocad_file_bounds_cb(void *param, OCADFile *file, OCADObjectEntry *entry) {
445 if (entry && entry->symbol && entry->ptr) {
446 ocad_file_bounds_union((ocad_file_bounds_data *)param, &(entry->rect));
447 }
448 return TRUE;
449 }
450
ocad_file_bounds(OCADFile * file,OCADRect * rect)451 bool ocad_file_bounds(OCADFile *file, OCADRect *rect) {
452 ocad_file_bounds_data data;
453 data.empty = TRUE;
454 ocad_object_entry_iterate(file, ocad_file_bounds_cb, &data);
455 if (!data.empty) {
456 memcpy(rect, &(data.rect), sizeof(OCADRect));
457 return TRUE;
458 }
459 return FALSE;
460 }
461