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