1 /*
2  * OriginAnyParser.cpp
3  *
4  * Copyright 2017 Miquel Garriga <gbmiquel@gmail.com>
5  *
6  * This program 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  * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Parser for all versions. Based mainly on Origin750Parser.cpp
20  */
21 
22 #include "OriginAnyParser.h"
23 #include <sstream>
24 #include <cinttypes>
25 
26 /* define a macro to get an int (or uint) from a istringstream in binary mode */
27 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
28 #define GET_SHORT(iss, ovalue)  {iss.read(reinterpret_cast<char *>(&ovalue), 2);};
29 #define GET_INT(iss, ovalue)    {iss.read(reinterpret_cast<char *>(&ovalue), 4);};
30 #define GET_FLOAT(iss, ovalue)  {iss.read(reinterpret_cast<char *>(&ovalue), 4);};
31 #define GET_DOUBLE(iss, ovalue) {iss.read(reinterpret_cast<char *>(&ovalue), 8);};
32 #else
swap_bytes(unsigned char * data,int size)33 void inline swap_bytes(unsigned char* data, int size) {int i = 0, j = size - 1; while(i < j) {std::swap(data[i], data[j]); ++i, --j;}}
34 #define GET_SHORT(iss, ovalue)  {iss.read(reinterpret_cast<char *>(&ovalue), 2); swap_bytes(reinterpret_cast<unsigned char *>(&ovalue), 2);};
35 #define GET_INT(iss, ovalue)    {iss.read(reinterpret_cast<char *>(&ovalue), 4); swap_bytes(reinterpret_cast<unsigned char *>(&ovalue), 4);};
36 #define GET_FLOAT(iss, ovalue)  {iss.read(reinterpret_cast<char *>(&ovalue), 4); swap_bytes(reinterpret_cast<unsigned char *>(&ovalue), 4);};
37 #define GET_DOUBLE(iss, ovalue) {iss.read(reinterpret_cast<char *>(&ovalue), 8); swap_bytes(reinterpret_cast<unsigned char *>(&ovalue), 8);};
38 #endif
39 
OriginAnyParser(const string & fileName)40 OriginAnyParser::OriginAnyParser(const string& fileName)
41 :	file(fileName.c_str(),ios::binary),
42 	logfile(nullptr),
43 	d_file_size(0),
44 	curpos(0),
45 	objectIndex(0),
46 	parseError(0),
47 	ispread(-1),
48 	imatrix(-1),
49 	iexcel(-1),
50 	igraph(-1),
51 	ilayer(-1) {
52 }
53 
parse()54 bool OriginAnyParser::parse() {
55 #ifdef GENERATE_CODE_FOR_LOG
56 	// append progress in log file
57 	logfile = fopen("opjfile.log","a");
58 #endif // GENERATE_CODE_FOR_LOG
59 
60 	// get length of file:
61 	file.seekg (0, ios_base::end);
62 	d_file_size = file.tellg();
63 	file.seekg(0, ios_base::beg);
64 
65 	LOG_PRINT(logfile, "File size: %" PRId64 "\n", d_file_size)
66 
67 	// get file and program version, check it is a valid file
68 	readFileVersion();
69 	if (parseError > 1) return false;
70 	curpos = file.tellg();
71 	LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
72 
73 	// get global header
74 	readGlobalHeader();
75 	if (parseError > 1) return false;
76 	curpos = file.tellg();
77 	LOG_PRINT(logfile, "Now at %"  PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
78 
79 	// get dataset list
80 	unsigned int dataset_list_size = 0;
81 	objectIndex = 0; // use it to count DataSets
82 
83 	LOG_PRINT(logfile, "Reading Data sets ...\n")
84 	while (true) {
85 		if (!readDataSetElement()) break;
86 		dataset_list_size++;
87 	}
88 	if (parseError > 1) return false;
89 	LOG_PRINT(logfile, " ... done. Data sets: %d\n", dataset_list_size)
90 	curpos = file.tellg();
91 	LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
92 
93 	for(unsigned int i = 0; i < spreadSheets.size(); ++i){
94 		if(spreadSheets[i].sheets > 1){
95 			LOG_PRINT(logfile, "		CONVERT SPREADSHEET \"%s\" to EXCEL\n", spreadSheets[i].name.c_str());
96 			convertSpreadToExcel(i);
97 			--i;
98 		}
99 	}
100 
101 	// get window list
102 	unsigned int window_list_size = 0;
103 	objectIndex = 0; // reset it to count Windows (except Notes)
104 
105 	LOG_PRINT(logfile, "Reading Windows ...\n")
106 	while (true) {
107 		if (!readWindowElement()) break;
108 		window_list_size++;
109 	}
110 	LOG_PRINT(logfile, " ... done. Windows: %d\n", window_list_size)
111 	curpos = file.tellg();
112 	LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
113 
114 	// get parameter list
115 	unsigned int parameter_list_size = 0;
116 
117 	LOG_PRINT(logfile, "Reading Parameters ...\n")
118 	while (true) {
119 		if (!readParameterElement()) break;
120 		parameter_list_size++;
121 	}
122 	LOG_PRINT(logfile, " ... done. Parameters: %d\n", parameter_list_size)
123 	curpos = file.tellg();
124 	LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
125 
126 	// Note windows were added between version >4.141 and 4.210,
127 	// i.e., with Release 5.0
128 	if (curpos < d_file_size) {
129 		// get note windows list
130 		unsigned int note_list_size = 0;
131 
132 		LOG_PRINT(logfile, "Reading Note windows ...\n")
133 		objectIndex = 0; // reset it to count Notes
134 		while (true) {
135 			if (!readNoteElement()) break;
136 			note_list_size++;
137 		}
138 		LOG_PRINT(logfile, " ... done. Note windows: %d\n", note_list_size)
139 		curpos = file.tellg();
140 		LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
141 	}
142 
143 	// Project Tree was added between version >4.210 and 4.2616,
144 	// i.e., with Release 6.0
145 	if (curpos < d_file_size) {
146 		// get project tree
147 		readProjectTree();
148 		curpos = file.tellg();
149 		LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
150 	}
151 
152 	// Attachments were added between version >4.2673_558 and 4.2764_623,
153 	// i.e., with Release 7.0
154 	if (curpos < d_file_size) {
155 		readAttachmentList();
156 		curpos = file.tellg();
157 		LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
158 	}
159 
160 	if (curpos >= d_file_size) LOG_PRINT(logfile, "Now at end of file\n")
161 
162 #ifdef GENERATE_CODE_FOR_LOG
163 	fclose(logfile);
164 #endif // GENERATE_CODE_FOR_LOG
165 
166 	return true;
167 }
168 
toLowerCase(string str)169 string toLowerCase(string str){
170 	for (unsigned int i = 0; i < str.length(); i++)
171 		if (str[i] >= 0x41 && str[i] <= 0x5A)
172 			str[i] = str[i] + 0x20;
173 	return str;
174 }
175 
createOriginAnyParser(const string & fileName)176 OriginParser* createOriginAnyParser(const string& fileName)
177 {
178 	return new OriginAnyParser(fileName);
179 }
180 
readObjectSize()181 unsigned int OriginAnyParser::readObjectSize() {
182 	unsigned int obj_size = 0;
183 
184 	char c = 0;
185 	file >> obj_size;
186 	file >> c;
187 	if (c != '\n') {
188 		curpos = file.tellg();
189 		LOG_PRINT(logfile, "Wrong delimiter %c at %" PRId64 " [0x%" PRIx64 "]\n", c, curpos, curpos)
190 		parseError = 3;
191 		return 0;
192 	}
193 	return obj_size;
194 }
195 
readObjectAsString(unsigned int size)196 string OriginAnyParser::readObjectAsString(unsigned int size) {
197 	char c;
198 	// read a size-byte blob of data followed by '\n'
199 	if (size > 0) {
200 		// get a string large enough to hold the result, initialize it to all 0's
201 		string blob = string(size, '\0');
202 		// read data into that string
203 		// cannot use '>>' operator because iendianfstream truncates it at first '\0'
204 		file.read(&blob[0], size);
205 		// read the '\n'
206 		file >> c;
207 		if (c != '\n') {
208 			curpos = file.tellg();
209 			LOG_PRINT(logfile, "Wrong delimiter %c at %" PRId64 " [0x%" PRIx64 "]\n", c, curpos, curpos)
210 			parseError = 4;
211 			return string();
212 		}
213 		return blob;
214 	}
215 	return string();
216 }
217 
readFileVersion()218 void OriginAnyParser::readFileVersion() {
219 	// get file and program version, check it is a valid file
220 	string sFileVersion;
221 	getline(file, sFileVersion);
222 
223 	if ((sFileVersion.substr(0,4) != "CPYA")) {
224 		LOG_PRINT(logfile, "File, is not a valid OPJ file\n")
225 		if ((sFileVersion.substr(0,5) != "CPYUA")) {
226 			LOG_PRINT(logfile, "File, is not a valid OPJU file\n")
227 			parseError = 2;
228 			return;
229 		}
230 	}
231 
232 	if (*sFileVersion.rbegin() != '#') parseError = 1;
233 	LOG_PRINT(logfile, "File version string: %s\n", sFileVersion.c_str())
234 }
235 
readGlobalHeader()236 void OriginAnyParser::readGlobalHeader() {
237 	// get global header size
238 	unsigned int gh_size = 0, gh_endmark = 0;
239 	gh_size = readObjectSize();
240 	curpos = file.tellg();
241 	LOG_PRINT(logfile, "Global header size: %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "],", gh_size, gh_size, curpos, curpos)
242 
243 	// get global header data
244 	string gh_data;
245 	gh_data = readObjectAsString(gh_size);
246 
247 	curpos = file.tellg();
248 	LOG_PRINT(logfile, " ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
249 
250 	// when gh_size > 0x1B, a double with fileVersion/100 can be read at gh_data[0x1B:0x23]
251 	if (gh_size > 0x1B) {
252 		istringstream stmp;
253 		stmp.str(gh_data.substr(0x1B));
254 		double dFileVersion;
255 		GET_DOUBLE(stmp, dFileVersion)
256 		if (dFileVersion > 8.5) {
257 			fileVersion = (unsigned int)trunc(dFileVersion*100.);
258 		} else {
259 			fileVersion = 10*(unsigned int)trunc(dFileVersion*10.);
260 		}
261 		LOG_PRINT(logfile, "Project version as read from header: %.2f (%.6f)\n", fileVersion/100.0, dFileVersion)
262 	}
263 
264 	// now read a zero size end mark
265 	gh_endmark = readObjectSize();
266 	if (gh_endmark != 0) {
267 		curpos = file.tellg();
268 		LOG_PRINT(logfile, "Wrong end of list mark %d at %" PRId64 " [0x%" PRIx64 "]\n", gh_endmark, curpos, curpos)
269 		parseError = 5;
270 		return;
271 	}
272 }
273 
readDataSetElement()274 bool OriginAnyParser::readDataSetElement() {
275 	/* get info and values of a DataSet (worksheet column, matrix sheet, ...)
276 	 * return true if a DataSet is found, otherwise return false */
277 	unsigned int dse_header_size = 0, dse_data_size = 0, dse_mask_size = 0;
278 	std::streamoff dsh_start = 0, dsd_start = 0, dsm_start = 0;
279 	string dse_header;
280 
281 	// get dataset header size
282 	dse_header_size = readObjectSize();
283 	if (dse_header_size == 0) return false;
284 
285 	curpos = file.tellg();
286 	dsh_start = curpos;
287 	LOG_PRINT(logfile, "Column: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "], ", dse_header_size, dse_header_size, curpos, curpos)
288 	dse_header = readObjectAsString(dse_header_size);
289 
290 	// get known info
291 	string name(25,0);
292 	name = dse_header.substr(0x58,25);
293 
294 	// go to end of dataset header, get data size
295 	file.seekg(dsh_start+dse_header_size+1, ios_base::beg);
296 	dse_data_size = readObjectSize();
297 	dsd_start = file.tellg();
298 	string dse_data = readObjectAsString(dse_data_size);
299 	curpos = file.tellg();
300 	LOG_PRINT(logfile, "data size %d [0x%X], from %" PRId64 " [0x%" PRIx64 "] to %" PRId64 " [0x%" PRIx64 "],", dse_data_size, dse_data_size, dsd_start, dsd_start, curpos, curpos)
301 
302 	// get data values
303 	getColumnInfoAndData(dse_header, dse_header_size, dse_data, dse_data_size);
304 
305 	// go to end of data values, get mask size (often zero)
306 	file.seekg(dsd_start+dse_data_size, ios_base::beg); // dse_data_size can be zero
307 	if (dse_data_size > 0) file.seekg(1, ios_base::cur);
308 	dse_mask_size = readObjectSize();
309 	dsm_start = file.tellg();
310 	if (dse_mask_size > 0) LOG_PRINT(logfile, "\nmask size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]", dse_mask_size, dse_mask_size, dsm_start, dsm_start)
311 	string dse_mask = readObjectAsString(dse_mask_size);
312 
313 	// get mask values
314 	if (dse_mask_size > 0) {
315 		curpos = file.tellg();
316 		LOG_PRINT(logfile, ", ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
317 		// TODO: extract mask values from dse_mask
318 		// go to end of dataset mask
319 		file.seekg(dsm_start+dse_mask_size+1, ios_base::beg);
320 	}
321 	curpos = file.tellg();
322 	LOG_PRINT(logfile, " ends at %" PRId64 " [0x%" PRIx64 "]: ", curpos, curpos)
323 	LOG_PRINT(logfile, "%s\n", name.c_str())
324 
325 	return true;
326 }
327 
readWindowElement()328 bool OriginAnyParser::readWindowElement() {
329 	/* get general info and details of a window
330 	 * return true if a Window is found, otherwise return false */
331 	unsigned int wde_header_size = 0;
332 	std::streamoff wdh_start = 0;
333 
334 	// get window header size
335 	wde_header_size = readObjectSize();
336 	if (wde_header_size == 0) return false;
337 
338 	curpos = file.tellg();
339 	wdh_start = curpos;
340 	LOG_PRINT(logfile, "Window found: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]: ", wde_header_size, wde_header_size, curpos, curpos)
341 	string wde_header = readObjectAsString(wde_header_size);
342 
343 	// get known info
344 	string name(25,0);
345 	name = wde_header.substr(0x02,25).c_str();
346 	LOG_PRINT(logfile, "%s\n", name.c_str())
347 
348 	// classify type of window
349 	ispread = findSpreadByName(name);
350 	imatrix = findMatrixByName(name);
351 	iexcel  = findExcelByName(name);
352 	igraph = -1;
353 
354 	if (ispread != -1) {
355 		LOG_PRINT(logfile, "\n  Window is a Worksheet book\n")
356 		getWindowProperties(spreadSheets[ispread], wde_header, wde_header_size);
357 	} else if (imatrix != -1) {
358 		LOG_PRINT(logfile, "\n  Window is a Matrix book\n")
359 		getWindowProperties(matrixes[imatrix], wde_header, wde_header_size);
360 	} else if (iexcel != -1) {
361 		LOG_PRINT(logfile, "\n  Window is an Excel book\n")
362 		getWindowProperties(excels[iexcel], wde_header, wde_header_size);
363 	} else {
364 		LOG_PRINT(logfile, "\n  Window is a Graph\n")
365 		graphs.push_back(Graph(name));
366 		igraph = (int)graphs.size()-1;
367 		getWindowProperties(graphs[igraph], wde_header, wde_header_size);
368 	}
369 
370 	// go to end of window header
371 	file.seekg(wdh_start+wde_header_size+1, ios_base::beg);
372 
373 	// get layer list
374 	unsigned int layer_list_size = 0;
375 
376 	LOG_PRINT(logfile, " Reading Layers ...\n")
377 	while (true) {
378 		ilayer = layer_list_size;
379 		if (!readLayerElement()) break;
380 		layer_list_size++;
381 	}
382 	LOG_PRINT(logfile, " ... done. Layers: %d\n", layer_list_size)
383 	curpos = file.tellg();
384 	LOG_PRINT(logfile, "window ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
385 
386 	return true;
387 }
388 
readLayerElement()389 bool OriginAnyParser::readLayerElement() {
390 	/* get general info and details of a layer
391 	 * return true if a Layer is found, otherwise return false */
392 	unsigned int lye_header_size = 0;
393 	std::streamoff lyh_start = 0;
394 
395 	// get layer header size
396 	lye_header_size = readObjectSize();
397 	if (lye_header_size == 0) return false;
398 
399 	curpos = file.tellg();
400 	lyh_start = curpos;
401 	LOG_PRINT(logfile, "  Layer found: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]\n", lye_header_size, lye_header_size, curpos, curpos)
402 	string lye_header = readObjectAsString(lye_header_size);
403 
404 	// get known info
405 	getLayerProperties(lye_header, lye_header_size);
406 
407 	// go to end of layer header
408 	file.seekg(lyh_start+lye_header_size+1, ios_base::beg);
409 
410 	// get annotation list
411 	unsigned int annotation_list_size = 0;
412 
413 	LOG_PRINT(logfile, "   Reading Annotations ...\n")
414 	/* Some annotations can be groups of annotations. We need a recursive function for those cases */
415 	annotation_list_size = readAnnotationList();
416 	if (annotation_list_size > 0) {
417 		LOG_PRINT(logfile, "   ... done. Annotations: %d\n", annotation_list_size)
418 	}
419 
420 	// get curve list
421 	unsigned int curve_list_size = 0;
422 
423 	LOG_PRINT(logfile, "   Reading Curves ...\n")
424 	while (true) {
425 		if (!readCurveElement()) break;
426 		curve_list_size++;
427 	}
428 	LOG_PRINT(logfile, "   ... done. Curves: %d\n", curve_list_size)
429 
430 	// get axisbreak list
431 	unsigned int axisbreak_list_size = 0;
432 
433 	LOG_PRINT(logfile, "   Reading Axis breaks ...\n")
434 	while (true) {
435 		if (!readAxisBreakElement()) break;
436 		axisbreak_list_size++;
437 	}
438 	LOG_PRINT(logfile, "   ... done. Axis breaks: %d\n", axisbreak_list_size)
439 
440 	// get x axisparameter list
441 	unsigned int axispar_x_list_size = 0;
442 
443 	LOG_PRINT(logfile, "   Reading x-Axis parameters ...\n")
444 	while (true) {
445 		if (!readAxisParameterElement(1)) break;
446 		axispar_x_list_size++;
447 	}
448 	LOG_PRINT(logfile, "   ... done. x-Axis parameters: %d\n", axispar_x_list_size)
449 
450 	// get y axisparameter list
451 	unsigned int axispar_y_list_size = 0;
452 
453 	LOG_PRINT(logfile, "   Reading y-Axis parameters ...\n")
454 	while (true) {
455 		if (!readAxisParameterElement(2)) break;
456 		axispar_y_list_size++;
457 	}
458 	LOG_PRINT(logfile, "   ... done. y-Axis parameters: %d\n", axispar_y_list_size)
459 
460 	// get z axisparameter list
461 	unsigned int axispar_z_list_size = 0;
462 
463 	LOG_PRINT(logfile, "   Reading z-Axis parameters ...\n")
464 	while (true) {
465 		if (!readAxisParameterElement(3)) break;
466 		axispar_z_list_size++;
467 	}
468 	LOG_PRINT(logfile, "   ... done. z-Axis parameters: %d\n", axispar_z_list_size)
469 
470 	curpos = file.tellg();
471 	LOG_PRINT(logfile, "  layer ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
472 
473 	return true;
474 }
475 
readAnnotationList()476 unsigned int OriginAnyParser::readAnnotationList() {
477 	/* Purpose of this function is to allow recursive call for groups of annotation elements. */
478 	unsigned int annotation_list_size = 0;
479 
480 	while (true) {
481 		if (!readAnnotationElement()) break;
482 		annotation_list_size++;
483 	}
484 	return annotation_list_size;
485 }
486 
readAnnotationElement()487 bool OriginAnyParser::readAnnotationElement() {
488 	/* get general info and details of an Annotation
489 	 * return true if an Annotation is found, otherwise return false */
490 	unsigned int ane_header_size = 0;
491 	std::streamoff anh_start = 0;
492 
493 	// get annotation header size
494 	ane_header_size = readObjectSize();
495 	if (ane_header_size == 0) return false;
496 
497 	curpos = file.tellg();
498 	anh_start = curpos;
499 	LOG_PRINT(logfile, "    Annotation found: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]: ", ane_header_size, ane_header_size, curpos, curpos)
500 	string ane_header = readObjectAsString(ane_header_size);
501 
502 	// get known info
503 	string name(41,0);
504 	name = ane_header.substr(0x46,41);
505 	LOG_PRINT(logfile, "%s\n", name.c_str())
506 
507 	// go to end of annotation header
508 	file.seekg(anh_start+ane_header_size+1, ios_base::beg);
509 
510 	// data of an annotation element is divided in three blocks
511 	// first block
512 	unsigned int ane_data_1_size = 0;
513 	std::streamoff andt1_start = 0;
514 	ane_data_1_size = readObjectSize();
515 
516 	andt1_start = file.tellg();
517 	LOG_PRINT(logfile, "     block 1 size %d [0x%X] at %" PRId64 " [0x%" PRIx64 "]\n", ane_data_1_size, ane_data_1_size, andt1_start, andt1_start)
518 	string andt1_data = readObjectAsString(ane_data_1_size);
519 
520 	// TODO: get known info
521 
522 	// go to end of first data block
523 	file.seekg(andt1_start+ane_data_1_size+1, ios_base::beg);
524 
525 	// second block
526 	unsigned int ane_data_2_size = 0;
527 	std::streamoff andt2_start = 0;
528 	ane_data_2_size = readObjectSize();
529 	andt2_start = file.tellg();
530 	LOG_PRINT(logfile, "     block 2 size %d [0x%X] at %" PRId64 " [0x%" PRIx64 "]\n", ane_data_2_size, ane_data_2_size, andt2_start, andt2_start)
531 	string andt2_data;
532 
533 	// check for group of annotations
534 	if (((ane_data_1_size == 0x5e) || (ane_data_1_size == 0x0A)) && (ane_data_2_size == 0x04)) {
535 		curpos = file.tellg();
536 		LOG_PRINT(logfile, "  Annotation group found at %" PRId64 " [0x%" PRIx64 "] ...\n", curpos, curpos)
537 		unsigned int angroup_size = readAnnotationList();
538 		curpos = file.tellg();
539 		if (angroup_size > 0) {
540 			LOG_PRINT(logfile, "  ... group end at %" PRId64 " [0x%" PRIx64 "]. Annotations: %d\n", curpos, curpos, angroup_size)
541 		}
542 		andt2_data = string();
543 	} else {
544 		andt2_data = readObjectAsString(ane_data_2_size);
545 		// TODO: get known info
546 		// go to end of second data block
547 		file.seekg(andt2_start+ane_data_2_size, ios_base::beg);
548 		if (ane_data_2_size > 0) file.seekg(1, ios_base::cur);
549 	}
550 
551 	// third block
552 	unsigned int ane_data_3_size = 0;
553 	ane_data_3_size = readObjectSize();
554 
555 	std::streamoff andt3_start = file.tellg();
556 	if (andt3_start > 0) {
557 		LOG_PRINT(logfile, "     block 3 size %d [0x%X] at %" PRId64 " [0x%" PRIx64 "]\n", ane_data_3_size, ane_data_3_size, andt3_start, andt3_start)
558 	}
559 	string andt3_data = readObjectAsString(ane_data_3_size);
560 
561 	curpos = file.tellg();
562 	LOG_PRINT(logfile, "    annotation ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
563 
564 	// get annotation info
565 	getAnnotationProperties(ane_header, ane_header_size, andt1_data, ane_data_1_size, andt2_data, ane_data_2_size, andt3_data, ane_data_3_size);
566 
567 	return true;
568 }
569 
readCurveElement()570 bool OriginAnyParser::readCurveElement() {
571 	/* get general info and details of a Curve
572 	 * return true if a Curve is found, otherwise return false */
573 	unsigned int cve_header_size = 0, cve_data_size = 0;
574 	std::streamoff cvh_start = 0, cvd_start = 0;
575 
576 	// get curve header size
577 	cve_header_size = readObjectSize();
578 	if (cve_header_size == 0) return false;
579 
580 	curpos = file.tellg();
581 	cvh_start = curpos;
582 	LOG_PRINT(logfile, "    Curve: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "], ", cve_header_size, cve_header_size, curpos, curpos)
583 	string cve_header = readObjectAsString(cve_header_size);
584 
585 	// TODO: get known info from curve header
586 	string name = cve_header.substr(0x12,12);
587 
588 	// go to end of header, get curve data size
589 	file.seekg(cvh_start+cve_header_size+1, ios_base::beg);
590 	cve_data_size = readObjectSize();
591 	cvd_start = file.tellg();
592 	LOG_PRINT(logfile, "data size %d [0x%X], from %" PRId64 " [0x%" PRIx64 "]", cve_data_size, cve_data_size, cvd_start, cvd_start)
593 	string cve_data = readObjectAsString(cve_data_size);
594 
595 	// TODO: get known info from curve data
596 
597 	// go to end of data
598 	file.seekg(cvd_start+cve_data_size, ios_base::beg);
599 	if (cve_data_size > 0) file.seekg(1, ios_base::cur);
600 
601 	curpos = file.tellg();
602 	LOG_PRINT(logfile, "to %" PRId64 " [0x%" PRIx64 "]: %s\n", curpos, curpos, name.c_str())
603 
604 	// get curve (or column) info
605 	getCurveProperties(cve_header, cve_header_size, cve_data, cve_data_size);
606 
607 	return true;
608 }
609 
readAxisBreakElement()610 bool OriginAnyParser::readAxisBreakElement() {
611 	/* get info of Axis breaks
612 	 * return true if an Axis break, otherwise return false */
613 	unsigned int abe_data_size = 0;
614 	std::streamoff abd_start = 0;
615 
616 	// get axis break data size
617 	abe_data_size = readObjectSize();
618 	if (abe_data_size == 0) return false;
619 
620 	curpos = file.tellg();
621 	abd_start = curpos;
622 	string abd_data = readObjectAsString(abe_data_size);
623 
624 	// get known info
625 
626 	// go to end of axis break data
627 	file.seekg(abd_start+abe_data_size+1, ios_base::beg);
628 
629 	// get axis break info
630 	getAxisBreakProperties(abd_data, abe_data_size);
631 
632 	return true;
633 }
634 
readAxisParameterElement(unsigned int naxis)635 bool OriginAnyParser::readAxisParameterElement(unsigned int naxis) {
636 	/* get info of Axis parameters for naxis-axis (x,y,z) = (1,2,3)
637 	 * return true if an Axis break is found, otherwise return false */
638 	unsigned int ape_data_size = 0;
639 	std::streamoff apd_start = 0;
640 
641 	// get axis break data size
642 	ape_data_size = readObjectSize();
643 	if (ape_data_size == 0) return false;
644 
645 	curpos = file.tellg();
646 	apd_start = curpos;
647 	string apd_data = readObjectAsString(ape_data_size);
648 
649 	// get known info
650 
651 	// go to end of axis break data
652 	file.seekg(apd_start+ape_data_size+1, ios_base::beg);
653 
654 	// get axis parameter info
655 	getAxisParameterProperties(apd_data, ape_data_size, naxis);
656 
657 	return true;
658 }
659 
readParameterElement()660 bool OriginAnyParser::readParameterElement() {
661 	// get parameter name
662 	string par_name;
663 	char c;
664 
665 	getline(file, par_name);
666 	if (par_name[0] == '\0') {
667 		unsigned int eof_parameters_mark = readObjectSize();
668 		if (eof_parameters_mark != 0) {
669 			LOG_PRINT(logfile, "Wrong end of parameters mark\n")
670 		}
671 		return false;
672 	}
673 	LOG_PRINT(logfile, " %s:", par_name.c_str())
674 	// get value
675 	double value;
676 	file >> value;
677 	LOG_PRINT(logfile, " %g\n", value)
678 	// read the '\n'
679 	file >> c;
680 	if (c != '\n') {
681 		curpos = file.tellg();
682 		LOG_PRINT(logfile, "Wrong delimiter %c at %" PRId64 " [0x%" PRIx64 "]\n", c, curpos, curpos)
683 		parseError = 6;
684 		return false;
685 	}
686 
687 	return true;
688 }
689 
readNoteElement()690 bool OriginAnyParser::readNoteElement() {
691 	/* get info of Note windows, including "Results Log"
692 	 * return true if a Note window is found, otherwise return false */
693 	unsigned int nwe_header_size = 0, nwe_label_size = 0, nwe_contents_size = 0;
694 	std::streamoff nwh_start = 0, nwl_start = 0, nwc_start = 0;
695 
696 	// get note header size
697 	nwe_header_size = readObjectSize();
698 	if (nwe_header_size == 0) return false;
699 
700 	curpos = file.tellg();
701 	nwh_start = curpos;
702 	LOG_PRINT(logfile, "  Note window found: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]\n", nwe_header_size, nwe_header_size, curpos, curpos)
703 	string nwe_header = readObjectAsString(nwe_header_size);
704 
705 	// TODO: get known info from header
706 
707 	// go to end of header
708 	file.seekg(nwh_start+nwe_header_size+1, ios_base::beg);
709 
710 	// get label size
711 	nwe_label_size = readObjectSize();
712 	nwl_start = file.tellg();
713 	string nwe_label = readObjectAsString(nwe_label_size);
714 	LOG_PRINT(logfile, "  label at %" PRId64 " [0x%" PRIx64 "]: %s\n", nwl_start, nwl_start, nwe_label.c_str())
715 
716 	// go to end of label
717 	file.seekg(nwl_start+nwe_label_size, ios_base::beg);
718 	if (nwe_label_size > 0) file.seekg(1, ios_base::cur);
719 
720 	// get contents size
721 	nwe_contents_size = readObjectSize();
722 	nwc_start = file.tellg();
723 	string nwe_contents = readObjectAsString(nwe_contents_size);
724 	if (nwc_start > 0) {
725 		LOG_PRINT(logfile, "  contents at %" PRId64 " [0x%" PRIx64 "]: \n%s\n", nwc_start, nwc_start, nwe_contents.c_str())
726 	}
727 
728 	// get note window info
729 	getNoteProperties(nwe_header, nwe_header_size, nwe_label, nwe_label_size, nwe_contents, nwe_contents_size);
730 
731 	return true;
732 }
733 
readProjectTree()734 void OriginAnyParser::readProjectTree() {
735 	unsigned int pte_depth = 0;
736 
737 	// first preamble size and data (usually 4)
738 	unsigned int pte_pre1_size = readObjectSize();
739 	string pte_pre1 = readObjectAsString(pte_pre1_size);
740 
741 	// second preamble size and data (usually 16)
742 	unsigned int pte_pre2_size = readObjectSize();
743 	string pte_pre2 = readObjectAsString(pte_pre2_size);
744 
745 	// root element and children
746 	unsigned int rootfolder = readFolderTree(
747 		projectTree.insert(projectTree.begin(), ProjectNode("", ProjectNode::Folder)),
748 		pte_depth
749 	);
750 	if (rootfolder > 0) {
751 		LOG_PRINT(logfile, "Number of files at root: %d\n", rootfolder)
752 	}
753 
754 	// epilogue (should be zero)
755 	unsigned int pte_post_size = readObjectSize();
756 	if (pte_post_size != 0) {
757 		LOG_PRINT(logfile, "Wrong end of project tree mark\n")
758 	}
759 
760 	// log info on project tree
761 #ifdef GENERATE_CODE_FOR_LOG
762 	outputProjectTree(cout);
763 #endif // GENERATE_CODE_FOR_LOG
764 
765 	return;
766 }
767 
readFolderTree(tree<ProjectNode>::iterator parent,unsigned int depth)768 unsigned int OriginAnyParser::readFolderTree(tree<ProjectNode>::iterator parent, unsigned int depth) {
769 	unsigned int fle_header_size = 0, fle_eofh_size = 0, fle_name_size = 0, fle_prop_size = 0;
770 
771 	// folder header size, data, end mark
772 	fle_header_size = readObjectSize();
773 	string fle_header = readObjectAsString(fle_header_size);
774 	fle_eofh_size = readObjectSize(); // (usually 0)
775 	if (fle_eofh_size != 0) {
776 		LOG_PRINT(logfile, "Wrong end of folder header mark")
777 	}
778 
779 	// folder name size
780 	fle_name_size = readObjectSize();
781 	curpos = file.tellg();
782 	string fle_name = readObjectAsString(fle_name_size);
783 	LOG_PRINT(logfile, "Folder name at %" PRId64 " [0x%" PRIx64 "]: %s\n", curpos, curpos, fle_name.c_str());
784 
785 	// additional properties
786 	fle_prop_size = readObjectSize();
787 	for (unsigned int i = 0; i < fle_prop_size; i++) {
788 		unsigned int obj_size = readObjectSize();
789 		string obj_data = readObjectAsString(obj_size);
790 	}
791 
792 	// get project folder properties
793 	tree<ProjectNode>::iterator current_folder = projectTree.append_child(parent, ProjectNode(fle_name, ProjectNode::Folder));
794 	getProjectFolderProperties(current_folder, fle_header, fle_header_size);
795 
796 	// file entries
797 	unsigned int number_of_files_size = 0;
798 
799 	number_of_files_size = readObjectSize(); // should be 4 as number_of_files is an integer
800 	curpos = file.tellg();
801 	LOG_PRINT(logfile, "Number of files at %" PRId64 " [0x%" PRIx64 "] ", curpos, curpos)
802 	string fle_nfiles = readObjectAsString(number_of_files_size);
803 
804 	istringstream stmp(ios_base::binary);
805 	stmp.str(fle_nfiles);
806 	unsigned int number_of_files = 0;
807 	GET_INT(stmp, number_of_files)
808 	LOG_PRINT(logfile, "%d\n", number_of_files)
809 
810 	for (unsigned int i=0; i < number_of_files; i++) {
811 		readProjectLeaf(current_folder);
812 	}
813 
814 	// subfolder entries
815 	unsigned int number_of_folders_size = 0;
816 
817 	number_of_folders_size = readObjectSize(); // should be 4 as number_of_subfolders is an integer
818 	curpos = file.tellg();
819 	LOG_PRINT(logfile, "Number of subfolders at %" PRId64 " [0x%" PRIx64 "] ", curpos, curpos)
820 	string fle_nfolders = readObjectAsString(number_of_folders_size);
821 
822 	stmp.str(fle_nfolders);
823 	unsigned int number_of_folders = 0;
824 	GET_INT(stmp, number_of_folders)
825 	LOG_PRINT(logfile, "%d\n", number_of_folders)
826 
827 	for (unsigned int i=0; i < number_of_folders; i++) {
828 		depth++;
829 		unsigned int files_in_subfolder = readFolderTree(current_folder, depth);
830 		if (files_in_subfolder > 0) {
831 			LOG_PRINT(logfile, "Number of files in subfolder: %d\n", files_in_subfolder)
832 		}
833 		depth--;
834 	}
835 
836 	return number_of_files;
837 }
838 
readProjectLeaf(tree<ProjectNode>::iterator current_folder)839 void OriginAnyParser::readProjectLeaf(tree<ProjectNode>::iterator current_folder) {
840 	// preamble size (usually 0) and data
841 	unsigned int ptl_pre_size = readObjectSize();
842 	string ptl_pre = readObjectAsString(ptl_pre_size);
843 
844 	// file data size (usually 8) and data
845 	unsigned int ptl_data_size = readObjectSize();
846 	curpos = file.tellg();
847 	string ptl_data = readObjectAsString(ptl_data_size);
848 	LOG_PRINT(logfile, "File at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
849 
850 	// epilogue (should be zero)
851 	unsigned int ptl_post_size = readObjectSize();
852 	if (ptl_post_size != 0) {
853 		LOG_PRINT(logfile, "Wrong end of project leaf mark\n")
854 	}
855 
856 	// get project node properties
857 	getProjectLeafProperties(current_folder, ptl_data, ptl_data_size);
858 
859 	return;
860 }
861 
readAttachmentList()862 void OriginAnyParser::readAttachmentList() {
863 	/* Attachments are divided in two groups (which can be empty)
864 	 first group is preceded by two integers: 4096 (0x1000) and number_of_attachments followed as usual by a '\n' mark
865 	 second group is a series of (header, name, data) triplets without the '\n' mark.
866 	*/
867 
868 	// figure out if first group is not empty. In this case we will read integer=8 at current file position
869 	unsigned int att_1st_empty = 0;
870 	file >> att_1st_empty;
871 	file.seekg(-4, ios_base::cur);
872 
873 	istringstream stmp(ios_base::binary);
874 	string att_header;
875 	if (att_1st_empty == 8) {
876 		// first group
877 		unsigned int att_list1_size = 0;
878 
879 		// get two integers
880 		// next line fails if first attachment group is empty: readObjectSize exits as there is no '\n' after 4 bytes for uint
881 		att_list1_size = readObjectSize(); // should be 8 as we expect two integer values
882 		curpos = file.tellg();
883 		string att_list1 = readObjectAsString(att_list1_size);
884 		LOG_PRINT(logfile, "First attachment group at %" PRId64 " [0x%" PRIx64 "]", curpos, curpos)
885 
886 		stmp.str(att_list1);
887 
888 		unsigned int att_mark = 0, number_of_atts = 0, iattno = 0, att_data_size = 0;
889 		GET_INT(stmp, att_mark) // should be 4096
890 		GET_INT(stmp, number_of_atts)
891 		LOG_PRINT(logfile, " with %d attachments.\n", number_of_atts)
892 
893 		for (unsigned int i=0; i < number_of_atts; i++) {
894 			/* Header is a group of 7 integers followed by \n
895 			1st  attachment mark (4096: 0x00 0x10 0x00 0x00)
896 			2nd  attachment number ( <num_of_att)
897 			3rd  attachment size
898 			4th .. 7th ???
899 			*/
900 			// get header
901 			att_header = readObjectAsString(7*4);
902 			stmp.str(att_header);
903 			GET_INT(stmp, att_mark) // should be 4096
904 			GET_INT(stmp, iattno)
905 			GET_INT(stmp, att_data_size)
906 			curpos = file.tellg();
907 			LOG_PRINT(logfile, "Attachment no %d (%d) at %" PRId64 " [0x%" PRIx64 "], size %d\n", i, iattno, curpos, curpos, att_data_size)
908 
909 			// get data
910 			string att_data = readObjectAsString(att_data_size);
911 			// even if att_data_size is zero, we get a '\n' mark
912 			if (att_data_size == 0) file.seekg(1, ios_base::cur);
913 		}
914 	} else {
915 		LOG_PRINT(logfile, "First attachment group is empty\n")
916 	}
917 
918 	/* Second group is a series of (header, name, data) triplets
919 	   There is no number of attachments. It ends when we reach EOF. */
920 	curpos = file.tellg();
921 	LOG_PRINT(logfile, "Second attachment group starts at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
922 	/* Header is a group of 3 integers, with no '\n' at end
923 		1st attachment header+name size including itself
924 		2nd attachment type (0x59 0x04 0xCA 0x7F for excel workbooks)
925 		3rd size of data */
926 
927 	// get header
928 	att_header = string(12,0);
929 	while (true) {
930 		// check for eof
931 		if ((d_file_size == file.tellg()) || (file.eof())) break;
932 		// cannot use readObjectAsString: there is no '\n' at end
933 		file.read(reinterpret_cast<char*>(&att_header[0]), 12);
934 
935 		if (file.gcount() != 12) break;
936 		// get header size, type and data size
937 		unsigned int att_header_size=0, att_type=0, att_size=0;
938 		stmp.str(att_header);
939 		GET_INT(stmp, att_header_size)
940 		GET_INT(stmp, att_type)
941 		GET_INT(stmp, att_size)
942 
943 		// get name and data
944 		unsigned int name_size = att_header_size - 3*4;
945 		string att_name = string(name_size, 0);
946 		file.read(&att_name[0], name_size);
947 		curpos = file.tellg();
948 		string att_data = string(att_size, 0);
949 		file.read(&att_data[0], att_size);
950 		LOG_PRINT(logfile, "attachment at %" PRId64 " [0x%" PRIx64 "], type 0x%X, size %d [0x%X]: %s\n", curpos, curpos, att_type, att_size, att_size, att_name.c_str())
951 	}
952 
953 	return;
954 }
955 
getColumnInfoAndData(const string & col_header,unsigned int col_header_size,const string & col_data,unsigned int col_data_size)956 bool OriginAnyParser::getColumnInfoAndData(const string& col_header, unsigned int col_header_size, const string& col_data, unsigned int col_data_size) {
957 	istringstream stmp(ios_base::binary);
958 	short data_type;
959 	char data_type_u;
960 	unsigned char valuesize;
961 	string name(25,0), column_name;
962 
963 	stmp.str(col_header.substr(0x16));
964 	GET_SHORT(stmp, data_type);
965 
966 	data_type_u = col_header[0x3F];
967 	if (fileVersion == 350) {
968 		valuesize = col_header[0x36];
969 	} else {
970 		valuesize = col_header[0x3D];
971 	}
972 	if(valuesize == 0) {
973 		LOG_PRINT(logfile, "	WARNING : found strange valuesize of %d\n", (int)valuesize);
974 		valuesize = 8;
975 	}
976 
977 	if (fileVersion == 350) {
978 		name = col_header.substr(0x57,25).c_str();
979 	} else {
980 		name = col_header.substr(0x58,25).c_str();
981 	}
982 	string dataset_name = name;
983 	string::size_type colpos = name.find_last_of("_");
984 
985 	if(colpos != string::npos){
986 		column_name = name.substr(colpos + 1);
987 		name.resize(colpos);
988 	}
989 
990 	LOG_PRINT(logfile, "\n  data_type 0x%.4X, data_type_u 0x%.2X, valuesize %d [0x%X], %s [%s]\n", data_type, data_type_u, valuesize, valuesize, name.c_str(), column_name.c_str());
991 
992 	int total_rows, first_row, last_row;
993 	stmp.str(col_header.substr(0x19));
994 	GET_INT(stmp, total_rows);
995 	GET_INT(stmp, first_row);
996 	GET_INT(stmp, last_row);
997 	LOG_PRINT(logfile, "  total %d, first %d, last %d rows\n", total_rows, first_row, last_row)
998 
999 	unsigned short signature;
1000 	if (col_header_size > 0x72) {
1001 		stmp.str(col_header.substr(0x71));
1002 		GET_SHORT(stmp, signature);
1003 	} else {
1004 		LOG_PRINT(logfile, "  NOTE: alternative signature determination\n")
1005 		signature = col_header[0x18];
1006 	}
1007 	LOG_PRINT(logfile, "  signature %d [0x%X], valuesize %d size %d ", signature, signature, valuesize, col_data_size)
1008 
1009 
1010 	size_t current_col = 1;//, nr = 0, nbytes = 0;
1011 	static unsigned int col_index = 0;
1012 	unsigned int current_sheet = 0;
1013 	vector<Origin::SpreadSheet>::difference_type spread = 0;
1014 
1015 	if (column_name.empty()) { // Matrix or function
1016 		if (data_type == 0x6081) { // Function
1017 			functions.push_back(Function(name, objectIndex));
1018 			Origin::Function &f = functions.back();
1019 			f.formula = toLowerCase(col_data.c_str());
1020 
1021 			stmp.str(col_header.substr(0x0A));
1022 			short t;
1023 			GET_SHORT(stmp, t)
1024 			if (t == 0x1194)
1025 				f.type = Function::Polar;
1026 
1027 			stmp.str(col_header.substr(0x21));
1028 			GET_INT(stmp, f.totalPoints)
1029 			GET_DOUBLE(stmp, f.begin)
1030 			double d;
1031 			GET_DOUBLE(stmp, d)
1032 			f.end = f.begin + d*(f.totalPoints - 1);
1033 
1034 			LOG_PRINT(logfile, "\n NEW FUNCTION: %s = %s", f.name.c_str(), f.formula.c_str());
1035 			LOG_PRINT(logfile, ". Range [%g : %g], number of points: %d\n", f.begin, f.end, f.totalPoints);
1036 
1037 		} else { // Matrix
1038 			vector<Origin::Matrix>::difference_type mIndex = -1;
1039 			string::size_type pos = name.find_first_of("@");
1040 			if (pos != string::npos){
1041 				string sheetName = name;
1042 				name.resize(pos);
1043 				mIndex = findMatrixByName(name);
1044 				if (mIndex != -1){
1045 					LOG_PRINT(logfile, "\n  NEW MATRIX SHEET\n");
1046 					matrixes[mIndex].sheets.push_back(MatrixSheet(sheetName, objectIndex));
1047 				}
1048 			} else {
1049 				LOG_PRINT(logfile, "\n  NEW MATRIX\n");
1050 				matrixes.push_back(Matrix(name));
1051 				matrixes.back().sheets.push_back(MatrixSheet(name, objectIndex));
1052 			}
1053 			// add an empty data set to keep objectIndex synchronized with datasets.size()
1054 			datasets.push_back(SpreadColumn(name,objectIndex));
1055 			getMatrixValues(col_data, col_data_size, data_type, data_type_u, valuesize, mIndex);
1056 		}
1057 	} else {
1058 		if(spreadSheets.size() == 0 || findSpreadByName(name) == -1) {
1059 			LOG_PRINT(logfile, "\n  NEW SPREADSHEET\n");
1060 			current_col = 1;
1061 			spreadSheets.push_back(SpreadSheet(name));
1062 			spread = spreadSheets.size() - 1;
1063 			spreadSheets.back().maxRows = 0;
1064 			current_sheet = 0;
1065 		} else {
1066 			spread = findSpreadByName(name);
1067 			current_col = spreadSheets[spread].columns.size();
1068 			if(!current_col)
1069 				current_col = 1;
1070 			++current_col;
1071 		}
1072 		spreadSheets[spread].columns.push_back(SpreadColumn(column_name, objectIndex));
1073 		spreadSheets[spread].columns.back().colIndex = ++col_index;
1074 		spreadSheets[spread].columns.back().dataset_name = dataset_name;
1075 		spreadSheets[spread].columns.back().numRows = total_rows;
1076 		spreadSheets[spread].columns.back().beginRow = first_row;
1077 		spreadSheets[spread].columns.back().endRow = last_row;
1078 
1079 		string::size_type sheetpos = spreadSheets[spread].columns.back().name.find_last_of("@");
1080 		if(sheetpos != string::npos){
1081 			unsigned int sheet = strtol(column_name.substr(sheetpos + 1).c_str(), nullptr, 10);
1082 			if( sheet > 1){
1083 				spreadSheets[spread].columns.back().name = column_name;
1084 
1085 				if (current_sheet != (sheet - 1))
1086 					current_sheet = sheet - 1;
1087 
1088 				spreadSheets[spread].columns.back().sheet = current_sheet;
1089 				if (spreadSheets[spread].sheets < sheet)
1090 					spreadSheets[spread].sheets = sheet;
1091 			}
1092 		}
1093 		LOG_PRINT(logfile, "  data index %d, valuesize %d, ", objectIndex, valuesize)
1094 
1095 		unsigned int nr = col_data_size / valuesize;
1096 		LOG_PRINT(logfile, "n. of rows = %d\n\n", nr)
1097 
1098 		spreadSheets[spread].maxRows<nr ? spreadSheets[spread].maxRows=nr : 0;
1099 		stmp.str(col_data);
1100 		for(unsigned int i = 0; i < nr; ++i)
1101 		{
1102 			double value;
1103 			if(valuesize <= 8)	// Numeric, Time, Date, Month, Day
1104 			{
1105 				GET_DOUBLE(stmp, value)
1106 				if ((i < 5) || (i > (nr-5))) {
1107 					LOG_PRINT(logfile, "%g ", value)
1108 				} else if (i == 5) {
1109 					LOG_PRINT(logfile, "... ")
1110 				}
1111 				spreadSheets[spread].columns[(current_col-1)].data.push_back(value);
1112 			}
1113 			else if((data_type & 0x100) == 0x100) // Text&Numeric
1114 			{
1115 				unsigned char c = col_data[i*valuesize];
1116 				stmp.seekg(i*valuesize+2, ios_base::beg);
1117 				if(c != 1) //value
1118 				{
1119 					GET_DOUBLE(stmp, value);
1120 					if ((i < 5) || (i > (nr-5))) {
1121 						LOG_PRINT(logfile, "%g ", value)
1122 					} else if (i == 5) {
1123 						LOG_PRINT(logfile, "... ")
1124 					}
1125 					spreadSheets[spread].columns[(current_col-1)].data.push_back(value);
1126 				}
1127 				else //text
1128 				{
1129 					string svaltmp = col_data.substr(i*valuesize+2, valuesize-2);
1130 					// TODO: check if this test is still needed
1131 					if(svaltmp.find(0x0E) != string::npos) { // try find non-printable symbol - garbage test
1132 						svaltmp = string();
1133 						LOG_PRINT(logfile, "Non printable symbol found, place 1 for i=%d\n", i)
1134 					}
1135 					if ((i < 5) || (i > (nr-5))) {
1136 						LOG_PRINT(logfile, "\"%s\" ", svaltmp.c_str())
1137 					} else if (i == 5) {
1138 						LOG_PRINT(logfile, "... ")
1139 					}
1140 					spreadSheets[spread].columns[(current_col-1)].data.push_back(svaltmp);
1141 				}
1142 			}
1143 			else //text
1144 			{
1145 				string svaltmp = col_data.substr(i*valuesize, valuesize).c_str();
1146 				// TODO: check if this test is still needed
1147 				if(svaltmp.find(0x0E) != string::npos) { // try find non-printable symbol - garbage test
1148 					svaltmp = string();
1149 					LOG_PRINT(logfile, "Non printable symbol found, place 2 for i=%d\n", i)
1150 				}
1151 				if ((i < 5) || (i > (nr-5))) {
1152 					LOG_PRINT(logfile, "\"%s\" ", svaltmp.c_str())
1153 				} else if (i == 5) {
1154 					LOG_PRINT(logfile, "... ")
1155 				}
1156 				spreadSheets[spread].columns[(current_col-1)].data.push_back(svaltmp);
1157 			}
1158 		}
1159 		LOG_PRINT(logfile, "\n\n")
1160 		datasets.push_back(spreadSheets[spread].columns.back());
1161 	}
1162 	++objectIndex;
1163 
1164 	return true;
1165 }
1166 
getMatrixValues(const string & col_data,unsigned int col_data_size,short data_type,char data_type_u,char valuesize,vector<Origin::Matrix>::difference_type mIndex)1167 void OriginAnyParser::getMatrixValues(const string& col_data, unsigned int col_data_size, short data_type, char data_type_u, char valuesize, vector<Origin::Matrix>::difference_type mIndex) {
1168 	if (matrixes.empty())
1169 		return;
1170 
1171 	istringstream stmp;
1172 	stmp.str(col_data);
1173 
1174 	if (mIndex < 0)
1175 		mIndex = (vector<Origin::Matrix>::difference_type)matrixes.size() - 1;
1176 
1177 	unsigned int size = col_data_size/valuesize;
1178 	bool logValues = true;
1179 	switch(data_type){
1180 		case 0x6001://double
1181 			for(unsigned int i = 0; i < size; ++i){
1182 				double value;
1183 				GET_DOUBLE(stmp, value)
1184 				matrixes[mIndex].sheets.back().data.push_back(value);
1185 			}
1186 			break;
1187 		case 0x6003://float
1188 			for(unsigned int i = 0; i < size; ++i){
1189 				float value;
1190 				GET_FLOAT(stmp, value)
1191 				matrixes[mIndex].sheets.back().data.push_back((double)value);
1192 			}
1193 			break;
1194 		case 0x6801://int
1195 			if (data_type_u == 8){//unsigned
1196 				for(unsigned int i = 0; i < size; ++i){
1197 					unsigned int value;
1198 					GET_INT(stmp, value)
1199 					matrixes[mIndex].sheets.back().data.push_back((double)value);
1200 				}
1201 			} else {
1202 				for(unsigned int i = 0; i < size; ++i){
1203 					int value;
1204 					GET_INT(stmp, value)
1205 					matrixes[mIndex].sheets.back().data.push_back((double)value);
1206 				}
1207 			}
1208 			break;
1209 		case 0x6803://short
1210 			if (data_type_u == 8){//unsigned
1211 				for(unsigned int i = 0; i < size; ++i){
1212 					unsigned short value;
1213 					GET_SHORT(stmp, value)
1214 					matrixes[mIndex].sheets.back().data.push_back((double)value);
1215 				}
1216 			} else {
1217 				for(unsigned int i = 0; i < size; ++i){
1218 					short value;
1219 					GET_SHORT(stmp, value)
1220 					matrixes[mIndex].sheets.back().data.push_back((double)value);
1221 				}
1222 			}
1223 			break;
1224 		case 0x6821://char
1225 			if (data_type_u == 8){//unsigned
1226 				for(unsigned int i = 0; i < size; ++i){
1227 					unsigned char value;
1228 					value = col_data[i];
1229 					matrixes[mIndex].sheets.back().data.push_back((double)value);
1230 				}
1231 			} else {
1232 				for(unsigned int i = 0; i < size; ++i){
1233 					char value;
1234 					value = col_data[i];
1235 					matrixes[mIndex].sheets.back().data.push_back((double)value);
1236 				}
1237 			}
1238 			break;
1239 		default:
1240 			LOG_PRINT(logfile, "	UNKNOWN MATRIX DATATYPE: %02X SKIP DATA\n", data_type);
1241 			matrixes.pop_back();
1242 			logValues = false;
1243 	}
1244 
1245 	if (logValues){
1246 		LOG_PRINT(logfile, "	FIRST 10 CELL VALUES: ");
1247 		for(unsigned int i = 0; i < 10 && i < matrixes[mIndex].sheets.back().data.size(); ++i)
1248 			LOG_PRINT(logfile, "%g\t", matrixes[mIndex].sheets.back().data[i]);
1249 	}
1250 }
1251 
getWindowProperties(Origin::Window & window,const string & wde_header,unsigned int wde_header_size)1252 void OriginAnyParser::getWindowProperties(Origin::Window& window, const string& wde_header, unsigned int wde_header_size) {
1253 	window.objectID = objectIndex;
1254 	++objectIndex;
1255 
1256 	istringstream stmp;
1257 
1258 	stmp.str(wde_header.substr(0x1B));
1259 	GET_SHORT(stmp, window.frameRect.left)
1260 	GET_SHORT(stmp, window.frameRect.top)
1261 	GET_SHORT(stmp, window.frameRect.right)
1262 	GET_SHORT(stmp, window.frameRect.bottom)
1263 
1264 	char c = wde_header[0x32];
1265 
1266 	if(c & 0x01)
1267 		window.state = Window::Minimized;
1268 	else if(c & 0x02)
1269 		window.state = Window::Maximized;
1270 
1271 	if (wde_header[0x42] != 0) {
1272 		window.windowBackgroundColorGradient = (ColorGradientDirection)(wde_header[0x42] >> 2);
1273 		window.windowBackgroundColorBase.type = Color::Regular;
1274 		window.windowBackgroundColorBase.regular = wde_header[0x43];
1275 		window.windowBackgroundColorEnd.type =  Color::Regular;
1276 		window.windowBackgroundColorEnd.regular = wde_header[0x44];
1277 	} else {
1278 		window.windowBackgroundColorGradient = ColorGradientDirection::NoGradient;
1279 		window.windowBackgroundColorBase.type = Color::Regular;
1280 		window.windowBackgroundColorBase.regular = Color::White;
1281 		window.windowBackgroundColorEnd.type =  Color::Regular;
1282 		window.windowBackgroundColorEnd.regular = Color::White;
1283 	}
1284 	LOG_PRINT(logfile, "ColorGradient %d, base %d, end %d\n",window.windowBackgroundColorGradient,
1285 		window.windowBackgroundColorBase.regular, window.windowBackgroundColorEnd.regular);
1286 
1287 	c = wde_header[0x69];
1288 
1289 	if(c & 0x01)
1290 		window.title = Window::Label;
1291 	else if(c & 0x02)
1292 		window.title = Window::Name;
1293 	else
1294 		window.title = Window::Both;
1295 
1296 	window.hidden = ((c & 0x08) != 0);
1297 	if (window.hidden) {
1298 		LOG_PRINT(logfile, "			WINDOW %d NAME : %s	is hidden\n", objectIndex, window.name.c_str());
1299 	} else {
1300 		LOG_PRINT(logfile, "			WINDOW %d NAME : %s	is not hidden\n", objectIndex, window.name.c_str());
1301 	}
1302 
1303 	if (wde_header_size > 0x82) {
1304 		// only projects of version 6.0 and higher have these
1305 		double creationDate, modificationDate;
1306 		stmp.str(wde_header.substr(0x73));
1307 		GET_DOUBLE(stmp, creationDate);
1308 		window.creationDate = doubleToPosixTime(creationDate);
1309 		GET_DOUBLE(stmp, modificationDate)
1310 		window.modificationDate = doubleToPosixTime(modificationDate);
1311 	}
1312 
1313 	if ((wde_header_size > 0x9E) && (wde_header[0x42] != 0)) {
1314 		// get window background colors for version > 5.0
1315 		window.windowBackgroundColorBase = getColor(wde_header.substr(0x97,4));
1316 		window.windowBackgroundColorEnd = getColor(wde_header.substr(0x9B,4));
1317 	}
1318 
1319 	if(wde_header_size > 0xC3){
1320 		window.label = wde_header.substr(0xC3).c_str();
1321 		window.label = window.label.substr(0,window.label.find("@${"));
1322 		LOG_PRINT(logfile, "			WINDOW %d LABEL: %s\n", objectIndex, window.label.c_str());
1323 	}
1324 
1325 	if (imatrix != -1) { // additional properties for matrix windows
1326 		unsigned char h = wde_header[0x29];
1327 		matrixes[imatrix].activeSheet = h;
1328 		if (wde_header_size > 0x86) {
1329 			h = wde_header[0x87];
1330 			matrixes[imatrix].header = (h == 194) ? Matrix::XY : Matrix::ColumnRow;
1331 		}
1332 	}
1333 	if (igraph != -1) { // additional properties for graph/layout windows
1334 		stmp.str(wde_header.substr(0x23));
1335 		GET_SHORT(stmp, graphs[igraph].width)
1336 		GET_SHORT(stmp, graphs[igraph].height)
1337 
1338 		unsigned char c = wde_header[0x38];
1339 		graphs[igraph].connectMissingData = ((c & 0x40) != 0);
1340 
1341 		string templateName = wde_header.substr(0x45,20).c_str();
1342 		graphs[igraph].templateName = templateName;
1343 		if (templateName == "LAYOUT") graphs[igraph].isLayout = true;
1344 	}
1345 }
1346 
getLayerProperties(const string & lye_header,unsigned int lye_header_size)1347 void OriginAnyParser::getLayerProperties(const string& lye_header, unsigned int lye_header_size) {
1348 	istringstream stmp;
1349 
1350 	if (ispread != -1) { // spreadsheet
1351 
1352 		spreadSheets[ispread].loose = false;
1353 
1354 	} else if (imatrix != -1) { // matrix
1355 
1356 		MatrixSheet& sheet = matrixes[imatrix].sheets[ilayer];
1357 
1358 		unsigned short width = 8;
1359 		stmp.str(lye_header.substr(0x27));
1360 		GET_SHORT(stmp, width)
1361 		if (width == 0) width = 8;
1362 		sheet.width = width;
1363 
1364 		stmp.str(lye_header.substr(0x2B));
1365 		GET_SHORT(stmp, sheet.columnCount)
1366 
1367 		stmp.str(lye_header.substr(0x52));
1368 		GET_SHORT(stmp, sheet.rowCount)
1369 
1370 		unsigned char view = lye_header[0x71];
1371 		if (view != 0x32 && view != 0x28){
1372 			sheet.view = MatrixSheet::ImageView;
1373 		} else {
1374 			sheet.view = MatrixSheet::DataView;
1375 		}
1376 
1377 		if (lye_header_size > 0xD2) {
1378 			sheet.name = lye_header.substr(0xD2,32).c_str();
1379 		}
1380 
1381 	} else if (iexcel != -1) { // excel
1382 
1383 		excels[iexcel].loose = false;
1384 		if (lye_header_size > 0xD2) {
1385 			excels[iexcel].sheets[ilayer].name = lye_header.substr(0xD2,32).c_str();
1386 		}
1387 
1388 	} else { // graph
1389 		graphs[igraph].layers.push_back(GraphLayer());
1390 		GraphLayer& glayer = graphs[igraph].layers[ilayer];
1391 
1392 		stmp.str(lye_header.substr(0x0F));
1393 		GET_DOUBLE(stmp, glayer.xAxis.min);
1394 		GET_DOUBLE(stmp, glayer.xAxis.max);
1395 		GET_DOUBLE(stmp, glayer.xAxis.step);
1396 
1397 		glayer.xAxis.majorTicks = lye_header[0x2B];
1398 
1399 		unsigned char g = lye_header[0x2D];
1400 		glayer.xAxis.zeroLine = ((g & 0x80) != 0);
1401 		glayer.xAxis.oppositeLine = ((g & 0x40) != 0);
1402 
1403 		glayer.xAxis.minorTicks = lye_header[0x37];
1404 		glayer.xAxis.scale = lye_header[0x38];
1405 
1406 		stmp.str(lye_header.substr(0x3A));
1407 		GET_DOUBLE(stmp, glayer.yAxis.min);
1408 		GET_DOUBLE(stmp, glayer.yAxis.max);
1409 		GET_DOUBLE(stmp, glayer.yAxis.step);
1410 
1411 		glayer.yAxis.majorTicks = lye_header[0x56];
1412 
1413 		g = lye_header[0x58];
1414 		glayer.yAxis.zeroLine = ((g & 0x80) != 0);
1415 		glayer.yAxis.oppositeLine = ((g & 0x40) != 0);
1416 
1417 		glayer.yAxis.minorTicks = lye_header[0x62];
1418 		glayer.yAxis.scale = lye_header[0x63];
1419 
1420 		g = lye_header[0x68];
1421 		glayer.gridOnTop = ((g & 0x04) != 0);
1422 		glayer.exchangedAxes = ((g & 0x40) != 0);
1423 
1424 		stmp.str(lye_header.substr(0x71));
1425 		GET_SHORT(stmp, glayer.clientRect.left)
1426 		GET_SHORT(stmp, glayer.clientRect.top)
1427 		GET_SHORT(stmp, glayer.clientRect.right)
1428 		GET_SHORT(stmp, glayer.clientRect.bottom)
1429 
1430 		unsigned char border = lye_header[0x89];
1431 		glayer.borderType = (BorderType)(border >= 0x80 ? border-0x80 : None);
1432 
1433 		if (lye_header_size > 0x107)
1434 			glayer.backgroundColor = getColor(lye_header.substr(0x105,4));
1435 
1436 	}
1437 }
1438 
getColor(const string & strbincolor)1439 Origin::Color OriginAnyParser::getColor(const string& strbincolor) {
1440 	/* decode a color value from a 4 byte binary string */
1441 	Origin::Color result;
1442 	unsigned char sbincolor[4];
1443 	for (int i=0; i < 4; i++) {
1444 		sbincolor[i] = strbincolor[i];
1445 	}
1446 	switch(sbincolor[3]) {
1447 		case 0:
1448 			if(sbincolor[0] < 0x64) {
1449 				result.type = Origin::Color::Regular;
1450 				result.regular = sbincolor[0];
1451 			} else {
1452 				switch(sbincolor[2]) {
1453 					case 0:
1454 						result.type = Origin::Color::Indexing;
1455 						break;
1456 					case 0x40:
1457 						result.type = Origin::Color::Mapping;
1458 						break;
1459 					case 0x80:
1460 						result.type = Origin::Color::RGB;
1461 						break;
1462 				}
1463 				result.column = sbincolor[0] - 0x64;
1464 			}
1465 			break;
1466 		case 1:
1467 			result.type = Origin::Color::Custom;
1468 			for(int i = 0; i < 3; ++i)
1469 				result.custom[i] = sbincolor[i];
1470 			break;
1471 		case 0x20:
1472 			result.type = Origin::Color::Increment;
1473 			result.starting = sbincolor[1];
1474 			break;
1475 		case 0xFF:
1476 			if(sbincolor[0] == 0xFC)
1477 				result.type = Origin::Color::None;
1478 			else if(sbincolor[0] == 0xF7)
1479 				result.type = Origin::Color::Automatic;
1480 			else {
1481 				result.type = Origin::Color::Regular;
1482 				result.regular = sbincolor[0];
1483 			}
1484 			break;
1485 		default:
1486 			result.type = Origin::Color::Regular;
1487 			result.regular = sbincolor[0];
1488 			break;
1489 	}
1490 	return result;
1491 }
1492 
getAnnotationProperties(const string & anhd,unsigned int anhdsz,const string & andt1,unsigned int andt1sz,const string & andt2,unsigned int andt2sz,const string & andt3,unsigned int andt3sz)1493 void OriginAnyParser::getAnnotationProperties(const string& anhd, unsigned int anhdsz, const string& andt1, unsigned int andt1sz, const string& andt2, unsigned int andt2sz, const string& andt3, unsigned int andt3sz) {
1494 	istringstream stmp;
1495 	(void) anhdsz; (void) andt3; (void) andt3sz;
1496 
1497 	if (ispread != -1) {
1498 
1499 		string sec_name = anhd.substr(0x46,41).c_str();
1500 		int col_index = findColumnByName((int)ispread, sec_name);
1501 		if (col_index != -1){ //check if it is a formula
1502 			spreadSheets[ispread].columns[col_index].command = andt1.c_str();
1503 			LOG_PRINT(logfile, "				Column: %s has formula: %s\n", sec_name.c_str(), spreadSheets[ispread].columns[col_index].command.c_str())
1504 		}
1505 
1506 	} else if (imatrix != -1) {
1507 
1508 		MatrixSheet& sheet = matrixes[imatrix].sheets[ilayer];
1509 		string sec_name = anhd.substr(0x46,41).c_str();
1510 
1511 		stmp.str(andt1.c_str());
1512 		if (sec_name == "MV") {
1513 			sheet.command = andt1.c_str();
1514 		} else if (sec_name == "Y2") {
1515 			stmp >> sheet.coordinates[0];
1516 		} else if (sec_name == "X2") {
1517 			stmp >> sheet.coordinates[1];
1518 		} else if (sec_name == "Y1") {
1519 			stmp >> sheet.coordinates[2];
1520 		} else if (sec_name == "X1") {
1521 			stmp >> sheet.coordinates[3];
1522 		} else if (sec_name == "COLORMAP") {
1523 			// Color maps for matrix annotations are similar to color maps for graph curves (3D).
1524 			// They differ only in the start offset to the data string.
1525 			getColorMap(sheet.colorMap, andt2, andt2sz);
1526 		}
1527 
1528 	} else if (iexcel != -1) {
1529 
1530 		string sec_name = anhd.substr(0x46,41).c_str();
1531 		vector<Origin::SpreadColumn>::difference_type col_index = findExcelColumnByName(iexcel, ilayer, sec_name);
1532 		if (col_index != -1){ //check if it is a formula
1533 			excels[iexcel].sheets[ilayer].columns[col_index].command = andt1.c_str();
1534 		}
1535 
1536 	} else {
1537 
1538 		GraphLayer& glayer = graphs[igraph].layers[ilayer];
1539 		string sec_name = anhd.substr(0x46,41).c_str();
1540 
1541 		Rect r;
1542 		stmp.str(anhd.substr(0x03));
1543 		GET_SHORT(stmp, r.left)
1544 		GET_SHORT(stmp, r.top)
1545 		GET_SHORT(stmp, r.right)
1546 		GET_SHORT(stmp, r.bottom)
1547 
1548 		unsigned char attach = anhd[0x28];
1549 		if (attach >= (unsigned char)Attach::End_) attach = Attach::Frame;
1550 		unsigned char border = anhd[0x29];
1551 
1552 		Color color = getColor(anhd.substr(0x33,4));
1553 
1554 		if (sec_name == "PL") glayer.yAxis.formatAxis[0].prefix = andt1.c_str();
1555 		if (sec_name == "PR") glayer.yAxis.formatAxis[1].prefix = andt1.c_str();
1556 		if (sec_name == "PB") glayer.xAxis.formatAxis[0].prefix = andt1.c_str();
1557 		if (sec_name == "PT") glayer.xAxis.formatAxis[1].prefix = andt1.c_str();
1558 		if (sec_name == "SL") glayer.yAxis.formatAxis[0].suffix = andt1.c_str();
1559 		if (sec_name == "SR") glayer.yAxis.formatAxis[1].suffix = andt1.c_str();
1560 		if (sec_name == "SB") glayer.xAxis.formatAxis[0].suffix = andt1.c_str();
1561 		if (sec_name == "ST") glayer.xAxis.formatAxis[1].suffix = andt1.c_str();
1562 		if (sec_name == "OL") glayer.yAxis.formatAxis[0].factor = andt1.c_str();
1563 		if (sec_name == "OR") glayer.yAxis.formatAxis[1].factor = andt1.c_str();
1564 		if (sec_name == "OB") glayer.xAxis.formatAxis[0].factor = andt1.c_str();
1565 		if (sec_name == "OT") glayer.xAxis.formatAxis[1].factor = andt1.c_str();
1566 
1567 		unsigned char type = andt1[0x00];
1568 		LineVertex begin, end;
1569 		/* OriginNNNParser identify line/arrow annotation by checking size of andt1
1570 		   Origin410: 21||24; Origin 500: 24; Origin 610: 24||96; Origin700 and higher: 120;
1571 		   An alternative is to look at anhd[0x02]:
1572 		     (0x21 for Circle/Rect, 0x22 for Line/Arrow, 0x23 for Polygon/Polyline)
1573 		 */
1574 		unsigned char ankind = anhd[0x02];
1575 		if (ankind == 0x22) {//Line/Arrow
1576 			if ((attach == Origin::Scale) && (andt1sz > 0x5F)) {
1577 				if (type == 2) {
1578 					stmp.str(andt1.substr(0x20));
1579 					GET_DOUBLE(stmp, begin.x)
1580 					GET_DOUBLE(stmp, end.x)
1581 					stmp.str(andt1.substr(0x40));
1582 					GET_DOUBLE(stmp, begin.y)
1583 					GET_DOUBLE(stmp, end.y)
1584 				} else if (type == 4) {//curved arrow: start point, 2 middle points and end point
1585 					stmp.str(andt1.substr(0x20));
1586 					GET_DOUBLE(stmp, begin.x)
1587 					GET_DOUBLE(stmp, end.x)
1588 					GET_DOUBLE(stmp, end.x)
1589 					GET_DOUBLE(stmp, end.x)
1590 					GET_DOUBLE(stmp, begin.y)
1591 					GET_DOUBLE(stmp, end.y)
1592 					GET_DOUBLE(stmp, end.y)
1593 					GET_DOUBLE(stmp, end.y)
1594 				}
1595 			} else {
1596 				short x1=0, x2=0, y1=0, y2=0;
1597 				if (type == 2) {//straight line/arrow
1598 					stmp.str(andt1.substr(0x01));
1599 					GET_SHORT(stmp, x1)
1600 					GET_SHORT(stmp, x2)
1601 					stmp.seekg(4, ios_base::cur);
1602 					GET_SHORT(stmp, y1)
1603 					GET_SHORT(stmp, y2)
1604 				} else if (type == 4) {//curved line/arrow has 4 points
1605 					stmp.str(andt1.substr(0x01));
1606 					GET_SHORT(stmp, x1)
1607 					stmp.seekg(4, ios_base::cur);
1608 					GET_SHORT(stmp, x2)
1609 					GET_SHORT(stmp, y1)
1610 					stmp.seekg(4, ios_base::cur);
1611 					GET_SHORT(stmp, y2)
1612 				}
1613 
1614 				double dx = fabs(x2 - x1);
1615 				double dy = fabs(y2 - y1);
1616 				double minx = (x1 <= x2) ? x1 : x2;
1617 				double miny = (y1 <= y2) ? y1 : y2;
1618 
1619 				begin.x = (x1 == x2) ? r.left + 0.5*r.width() : r.left + (x1 - minx)/dx*r.width();
1620 				end.x   = (x1 == x2) ? r.left + 0.5*r.width() : r.left + (x2 - minx)/dx*r.width();
1621 				begin.y = (y1 == y2) ? r.top  + 0.5*r.height(): r.top + (y1 - miny)/dy*r.height();
1622 				end.y   = (y1 == y2) ? r.top  + 0.5*r.height(): r.top + (y2 - miny)/dy*r.height();
1623 			}
1624 			unsigned char arrows = andt1[0x11];
1625 			switch (arrows) {
1626 				case 0:
1627 					begin.shapeType = 0;
1628 					end.shapeType = 0;
1629 					break;
1630 				case 1:
1631 					begin.shapeType = 1;
1632 					end.shapeType = 0;
1633 					break;
1634 				case 2:
1635 					begin.shapeType = 0;
1636 					end.shapeType = 1;
1637 					break;
1638 				case 3:
1639 					begin.shapeType = 1;
1640 					end.shapeType = 1;
1641 					break;
1642 			}
1643 			if (andt1sz > 0x77) {
1644 				begin.shapeType = andt1[0x60];
1645 				unsigned int w = 0;
1646 				stmp.str(andt1.substr(0x64));
1647 				GET_INT(stmp, w)
1648 				begin.shapeWidth = (double)w/500.0;
1649 				GET_INT(stmp, w)
1650 				begin.shapeLength = (double)w/500.0;
1651 
1652 				end.shapeType = andt1[0x6C];
1653 				stmp.str(andt1.substr(0x70));
1654 				GET_INT(stmp, w)
1655 				end.shapeWidth = (double)w/500.0;
1656 				GET_INT(stmp, w)
1657 				end.shapeLength = (double)w/500.0;
1658 			}
1659 		}
1660 		//text properties
1661 		short rotation;
1662 		stmp.str(andt1.substr(0x02));
1663 		GET_SHORT(stmp, rotation)
1664 		unsigned char fontSize = andt1[0x4];
1665 		unsigned char tab = andt1[0x0A];
1666 
1667 		//line properties
1668 		unsigned char lineStyle = andt1[0x12];
1669 		unsigned short w1 = 0;
1670 		if (andt1sz > 0x14) {
1671 			stmp.str(andt1.substr(0x13));
1672 			GET_SHORT(stmp, w1)
1673 		}
1674 		double width = (double)w1/500.0;
1675 
1676 		Figure figure;
1677 		stmp.str(andt1.substr(0x05));
1678 		GET_SHORT(stmp, w1)
1679 		figure.width = (double)w1/500.0;
1680 		figure.style = andt1[0x08];
1681 
1682 		if (andt1sz > 0x4D) {
1683 			figure.fillAreaColor = getColor(andt1.substr(0x42,4));
1684 			stmp.str(andt1.substr(0x46));
1685 			GET_SHORT(stmp, w1)
1686 			figure.fillAreaPatternWidth = (double)w1/500.0;
1687 			figure.fillAreaPatternColor = getColor(andt1.substr(0x4A,4));
1688 			figure.fillAreaPattern = andt1[0x4E];
1689 		}
1690 		if (andt1sz > 0x56) {
1691 			unsigned char h = andt1[0x57];
1692 			figure.useBorderColor = (h == 0x10);
1693 		}
1694 
1695 		if (sec_name == "XB") {
1696 			string text = andt2.c_str();
1697 			glayer.xAxis.position = GraphAxis::Bottom;
1698 			glayer.xAxis.formatAxis[0].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1699 		}
1700 		else if (sec_name == "XT") {
1701 			string text = andt2.c_str();
1702 			glayer.xAxis.position = GraphAxis::Top;
1703 			glayer.xAxis.formatAxis[1].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1704 		}
1705 		else if (sec_name == "YL") {
1706 			string text = andt2.c_str();
1707 			glayer.yAxis.position = GraphAxis::Left;
1708 			glayer.yAxis.formatAxis[0].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1709 		}
1710 		else if (sec_name == "YR") {
1711 			string text = andt2.c_str();
1712 			glayer.yAxis.position = GraphAxis::Right;
1713 			glayer.yAxis.formatAxis[1].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1714 		}
1715 		else if (sec_name == "ZF") {
1716 			string text = andt2.c_str();
1717 			glayer.zAxis.position = GraphAxis::Front;
1718 			glayer.zAxis.formatAxis[0].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1719 		}
1720 		else if (sec_name == "ZB") {
1721 			string text = andt2.c_str();
1722 			glayer.zAxis.position = GraphAxis::Back;
1723 			glayer.zAxis.formatAxis[1].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1724 		}
1725 		else if (sec_name == "3D") {
1726 			stmp.str(andt2);
1727 			GET_DOUBLE(stmp, glayer.zAxis.min)
1728 			GET_DOUBLE(stmp, glayer.zAxis.max)
1729 			GET_DOUBLE(stmp, glayer.zAxis.step)
1730 			glayer.zAxis.majorTicks = andt2[0x1C];
1731 			glayer.zAxis.minorTicks = andt2[0x28];
1732 			glayer.zAxis.scale = andt2[0x29];
1733 
1734 			stmp.str(andt2.substr(0x5A));
1735 			GET_FLOAT(stmp, glayer.xAngle)
1736 			GET_FLOAT(stmp, glayer.yAngle)
1737 			GET_FLOAT(stmp, glayer.zAngle)
1738 
1739 			stmp.str(andt2.substr(0x218));
1740 			GET_FLOAT(stmp, glayer.xLength)
1741 			GET_FLOAT(stmp, glayer.yLength)
1742 			GET_FLOAT(stmp, glayer.zLength)
1743 			glayer.xLength /= 23.0;
1744 			glayer.yLength /= 23.0;
1745 			glayer.zLength /= 23.0;
1746 
1747 			glayer.orthographic3D = (andt2[0x240] != 0);
1748 		}
1749 		else if (sec_name == "Legend") {
1750 			string text = andt2.c_str();
1751 			glayer.legend = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1752 		}
1753 		else if (sec_name == "__BCO2") { // histogram
1754 			stmp.str(andt2.substr(0x10));
1755 			GET_DOUBLE(stmp, glayer.histogramBin)
1756 			stmp.str(andt2.substr(0x20));
1757 			GET_DOUBLE(stmp, glayer.histogramEnd)
1758 			GET_DOUBLE(stmp, glayer.histogramBegin)
1759 
1760 			// TODO: check if 0x5E is right (obtained from anhdsz-0x46+93-andt1sz = 111-70+93-40 = 94)
1761 			glayer.percentile.p1SymbolType = andt2[0x5E];
1762 			glayer.percentile.p99SymbolType = andt2[0x5F];
1763 			glayer.percentile.meanSymbolType = andt2[0x60];
1764 			glayer.percentile.maxSymbolType = andt2[0x61];
1765 			glayer.percentile.minSymbolType = andt2[0x62];
1766 
1767 			// 0x9F = 0x5E+65
1768 			glayer.percentile.labels = andt2[0x9F];
1769 			// 0x6B = 0x5E+106-93 = 107
1770 			glayer.percentile.whiskersRange = andt2[0x6B];
1771 			glayer.percentile.boxRange = andt2[0x6C];
1772 			// 0x8e = 0x5E+141-93 = 142
1773 			glayer.percentile.whiskersCoeff = andt2[0x8e];
1774 			glayer.percentile.boxCoeff = andt2[0x8f];
1775 			unsigned char h = andt2[0x90];
1776 			glayer.percentile.diamondBox = (h == 0x82) ? true : false;
1777 			// 0xCB = 0x5E+109 = 203
1778 			stmp.str(andt2.substr(0xCB));
1779 			GET_SHORT(stmp, glayer.percentile.symbolSize)
1780 			glayer.percentile.symbolSize = glayer.percentile.symbolSize/2 + 1;
1781 			// 0x101 = 0x5E+163
1782 			glayer.percentile.symbolColor = getColor(andt2.substr(0x101,4));
1783 			glayer.percentile.symbolFillColor = getColor(andt2.substr(0x105,4));
1784 		}
1785 		else if (sec_name == "_206") { // box plot labels
1786 		}
1787 		else if (sec_name == "VLine") {
1788 			stmp.str(andt1.substr(0x0A));
1789 			double start;
1790 			GET_DOUBLE(stmp, start)
1791 			stmp.str(andt1.substr(0x1A));
1792 			double width;
1793 			GET_DOUBLE(stmp, width)
1794 			glayer.vLine = start + 0.5*width;
1795 			glayer.imageProfileTool = 2;
1796 		}
1797 		else if (sec_name == "HLine") {
1798 			stmp.str(andt1.substr(0x12));
1799 			double start;
1800 			GET_DOUBLE(stmp, start)
1801 			stmp.str(andt1.substr(0x22));
1802 			double width;
1803 			GET_DOUBLE(stmp, width)
1804 			glayer.hLine = start + 0.5*width;
1805 			glayer.imageProfileTool = 2;
1806 		}
1807 		else if (sec_name == "vline") {
1808 			stmp.str(andt1.substr(0x20));
1809 			GET_DOUBLE(stmp, glayer.vLine)
1810 			glayer.imageProfileTool = 1;
1811 		}
1812 		else if (sec_name == "hline") {
1813 			stmp.str(andt1.substr(0x40));
1814 			GET_DOUBLE(stmp, glayer.hLine)
1815 			glayer.imageProfileTool = 1;
1816 		}
1817 		else if (sec_name == "ZCOLORS") {
1818 			glayer.isXYY3D = true;
1819 			if (fileVersion < 600) {
1820 				ColorMap& colorMap = glayer.colorMap;
1821 				getZcolorsMap(colorMap, andt2, andt2sz);
1822 			}
1823 		}
1824 		else if (sec_name == "SPECTRUM1") {
1825 			glayer.isXYY3D = false;
1826 			glayer.colorScale.visible = true;
1827 			glayer.colorScale.reverseOrder = (andt2[0x18] != 0);
1828 			stmp.str(andt2.substr(0x20));
1829 			GET_SHORT(stmp, glayer.colorScale.colorBarThickness)
1830 			GET_SHORT(stmp, glayer.colorScale.labelGap)
1831 			glayer.colorScale.labelsColor = getColor(andt2.substr(0x5C,4));
1832 		}
1833 		else if (sec_name == "&0") {
1834 			glayer.isWaterfall = true;
1835 			string text = andt1.c_str();
1836 			string::size_type commaPos = text.find_first_of(",");
1837 			stmp.str(text.substr(0,commaPos));
1838 			stmp >> glayer.xOffset;
1839 			stmp.str(text.substr(commaPos+1));
1840 			stmp >> glayer.yOffset;
1841 		}
1842 		/* OriginNNNParser identify text, circle, rectangle and bitmap annotation by checking size of andt1:
1843 		             text/pie text          rectangle/circle       line            bitmap
1844 		   Origin410: 22                    0xA(10)                21/24           38
1845 		   Origin500: 22                    0xA(10)                24              40
1846 		   Origin610: 22                    0xA(10)                24/96           40
1847 		   Origin700:                       0x5E(94)               120             0x28(40)
1848 		   Origin750: 0x3E(62)/78           0x5E(94)              0x78(120)        0x28(40)
1849 		   Origin850: 0x3E(62)/78           0x5E(94)              0x78(120)        0x28(40)
1850 		   An alternative is to look at anhd[0x02]:
1851 		     (0x00 for Text, 0x21 for Circle/Rect, 0x22 for Line/Arrow, 0x23 for Polygon/Polyline)
1852 		*/
1853 		else if ((ankind == 0x0) && (sec_name != "DelData")) { // text
1854 			string text = andt2.c_str();
1855 			if (sec_name.substr(0,3) == "PIE")
1856 				glayer.pieTexts.push_back(TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach));
1857 			else
1858 				glayer.texts.push_back(TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach));
1859 		}
1860 		else if (ankind == 0x21) { // rectangle & circle
1861 			switch (type) { // type = andt1[0x00]
1862 				case 0:
1863 				case 1:
1864 					figure.type = Figure::Rectangle;
1865 					break;
1866 				case 2:
1867 				case 3:
1868 					figure.type = Figure::Circle;
1869 					break;
1870 			}
1871 			figure.clientRect = r;
1872 			figure.attach = (Attach)attach;
1873 			figure.color = color;
1874 
1875 			glayer.figures.push_back(figure);
1876 		}
1877 		else if ((ankind == 0x22) && (sec_name != "sLine") && (sec_name != "sline")) { // line/arrow
1878 			glayer.lines.push_back(Line());
1879 			Line& line(glayer.lines.back());
1880 			line.color = color;
1881 			line.clientRect = r;
1882 			line.attach = (Attach)attach;
1883 			line.width = width;
1884 			line.style = lineStyle;
1885 			line.begin = begin;
1886 			line.end = end;
1887 		}
1888 		else if (andt1sz == 40) { // bitmap
1889 			if (type == 4) { // type = andt1[0x00]
1890 				unsigned long filesize = andt2sz + 14;
1891 				glayer.bitmaps.push_back(Bitmap());
1892 				Bitmap& bitmap(glayer.bitmaps.back());
1893 				bitmap.clientRect = r;
1894 				bitmap.attach = (Attach)attach;
1895 				bitmap.size = filesize;
1896 				bitmap.borderType = (BorderType)(border >= 0x80 ? border-0x80 : None);
1897 				bitmap.data = new unsigned char[filesize];
1898 				unsigned char* data = bitmap.data;
1899 				//add Bitmap header
1900 				memcpy(data, "BM", 2);
1901 				data += 2;
1902 				memcpy(data, &filesize, 4);
1903 				data += 4;
1904 				unsigned int d = 0;
1905 				memcpy(data, &d, 4);
1906 				data += 4;
1907 				d = 0x36;
1908 				memcpy(data, &d, 4);
1909 				data += 4;
1910 				memcpy(data, andt2.c_str(), andt2sz);
1911 			} else if (type == 6) {
1912 				// TODO check if 0x5E is right (obtained from anhdsz-0x46+93-andt1sz = 111-70+93-40 = 94)
1913 				string gname = andt2.substr(0x5E).c_str();
1914 				glayer.bitmaps.push_back(Bitmap(gname));
1915 				Bitmap& bitmap(glayer.bitmaps.back());
1916 				bitmap.clientRect = r;
1917 				bitmap.attach = (Attach)attach;
1918 				bitmap.size = 0;
1919 				bitmap.borderType = (BorderType)(border >= 0x80 ? border-0x80 : None);
1920 			}
1921 		}
1922 
1923 	}
1924 	return;
1925 }
1926 
getCurveProperties(const string & cvehd,unsigned int cvehdsz,const string & cvedt,unsigned int cvedtsz)1927 void OriginAnyParser::getCurveProperties(const string& cvehd, unsigned int cvehdsz, const string& cvedt, unsigned int cvedtsz) {
1928 	istringstream stmp;
1929 
1930 	if (ispread != -1) { // spreadsheet: curves are columns
1931 
1932 		// TODO: check that spreadsheet columns are stored in proper order
1933 		// vector<SpreadColumn> header;
1934 		unsigned char c = cvehd[0x11];
1935 		string name = cvehd.substr(0x12).c_str();
1936 		unsigned short width = 0;
1937 		if (cvehdsz > 0x4B) {
1938 			stmp.str(cvehd.substr(0x4A));
1939 			GET_SHORT(stmp, width)
1940 		}
1941 		int col_index = findColumnByName((int)ispread, name);
1942 		if (col_index != -1) {
1943 			if (spreadSheets[ispread].columns[col_index].name != name)
1944 				spreadSheets[ispread].columns[col_index].name = name;
1945 
1946 			SpreadColumn::ColumnType type;
1947 			switch(c){
1948 				case 3:
1949 					type = SpreadColumn::X;
1950 					break;
1951 				case 0:
1952 					type = SpreadColumn::Y;
1953 					break;
1954 				case 5:
1955 					type = SpreadColumn::Z;
1956 					break;
1957 				case 6:
1958 					type = SpreadColumn::XErr;
1959 					break;
1960 				case 2:
1961 					type = SpreadColumn::YErr;
1962 					break;
1963 				case 4:
1964 					type = SpreadColumn::Label;
1965 					break;
1966 				default:
1967 					type = SpreadColumn::NONE;
1968 					break;
1969 			}
1970 			spreadSheets[ispread].columns[col_index].type = type;
1971 
1972 			width /= 0xA;
1973 			if(width == 0) width = 8;
1974 			spreadSheets[ispread].columns[col_index].width = width;
1975 			unsigned char c1 = cvehd[0x1E];
1976 			unsigned char c2 = cvehd[0x1F];
1977 			switch (c1) {
1978 				case 0x00: // Numeric	   - Dec1000
1979 				case 0x09: // Text&Numeric - Dec1000
1980 				case 0x10: // Numeric	   - Scientific
1981 				case 0x19: // Text&Numeric - Scientific
1982 				case 0x20: // Numeric	   - Engineering
1983 				case 0x29: // Text&Numeric - Engineering
1984 				case 0x30: // Numeric	   - Dec1,000
1985 				case 0x39: // Text&Numeric - Dec1,000
1986 					spreadSheets[ispread].columns[col_index].valueType = (c1%0x10 == 0x9) ? TextNumeric : Numeric;
1987 					spreadSheets[ispread].columns[col_index].valueTypeSpecification = c1 / 0x10;
1988 					if (c2 >= 0x80) {
1989 						spreadSheets[ispread].columns[col_index].significantDigits = c2 - 0x80;
1990 						spreadSheets[ispread].columns[col_index].numericDisplayType = SignificantDigits;
1991 					} else if (c2 > 0) {
1992 						spreadSheets[ispread].columns[col_index].decimalPlaces = c2 - 0x03;
1993 						spreadSheets[ispread].columns[col_index].numericDisplayType = DecimalPlaces;
1994 					}
1995 					break;
1996 				case 0x02: // Time
1997 					spreadSheets[ispread].columns[col_index].valueType = Time;
1998 					spreadSheets[ispread].columns[col_index].valueTypeSpecification = c2 - 0x80;
1999 					break;
2000 				case 0x03: // Date
2001 				case 0x33:
2002 					spreadSheets[ispread].columns[col_index].valueType = Date;
2003 					spreadSheets[ispread].columns[col_index].valueTypeSpecification= c2 - 0x80;
2004 					break;
2005 				case 0x31: // Text
2006 					spreadSheets[ispread].columns[col_index].valueType = Text;
2007 					break;
2008 				case 0x4: // Month
2009 				case 0x34:
2010 					spreadSheets[ispread].columns[col_index].valueType = Month;
2011 					spreadSheets[ispread].columns[col_index].valueTypeSpecification = c2;
2012 					break;
2013 				case 0x5: // Day
2014 				case 0x35:
2015 					spreadSheets[ispread].columns[col_index].valueType = Day;
2016 					spreadSheets[ispread].columns[col_index].valueTypeSpecification = c2;
2017 					break;
2018 				default: // Text
2019 					spreadSheets[ispread].columns[col_index].valueType = Text;
2020 					break;
2021 			}
2022 			if (cvedtsz > 0) {
2023 				spreadSheets[ispread].columns[col_index].comment = cvedt.c_str();
2024 			}
2025 			// TODO: check that spreadsheet columns are stored in proper order
2026 			// header.push_back(spreadSheets[ispread].columns[col_index]);
2027 		}
2028 		// TODO: check that spreadsheet columns are stored in proper order
2029 		// for (unsigned int i = 0; i < header.size(); i++)
2030 		// 	spreadSheets[spread].columns[i] = header[i];
2031 
2032 	} else if (imatrix != -1) {
2033 
2034 		MatrixSheet sheet = matrixes[imatrix].sheets[ilayer];
2035 		unsigned char c1 = cvehd[0x1E];
2036 		unsigned char c2 = cvehd[0x1F];
2037 
2038 		sheet.valueTypeSpecification = c1/0x10;
2039 		if (c2 >= 0x80) {
2040 			sheet.significantDigits = c2-0x80;
2041 			sheet.numericDisplayType = SignificantDigits;
2042 		} else if (c2 > 0) {
2043 			sheet.decimalPlaces = c2-0x03;
2044 			sheet.numericDisplayType = DecimalPlaces;
2045 		}
2046 
2047 		matrixes[imatrix].sheets[ilayer] = sheet;
2048 
2049 	} else if (iexcel != -1) {
2050 
2051 		unsigned char c = cvehd[0x11];
2052 		string name = cvehd.substr(0x12).c_str();
2053 		unsigned short width = 0;
2054 		stmp.str(cvehd.substr(0x4A));
2055 		GET_SHORT(stmp, width)
2056 		unsigned short dataID = 0;
2057 		stmp.str(cvehd.substr(0x04));
2058 		GET_SHORT(stmp, dataID)
2059 
2060 		unsigned int isheet = datasets[dataID-1].sheet;
2061 		vector<Origin::SpreadColumn>::difference_type col_index = findExcelColumnByName(iexcel, isheet, name);
2062 		if (col_index != -1) {
2063 			SpreadColumn::ColumnType type;
2064 			switch(c){
2065 				case 3:
2066 					type = SpreadColumn::X;
2067 					break;
2068 				case 0:
2069 					type = SpreadColumn::Y;
2070 					break;
2071 				case 5:
2072 					type = SpreadColumn::Z;
2073 					break;
2074 				case 6:
2075 					type = SpreadColumn::XErr;
2076 					break;
2077 				case 2:
2078 					type = SpreadColumn::YErr;
2079 					break;
2080 				case 4:
2081 					type = SpreadColumn::Label;
2082 					break;
2083 				default:
2084 					type = SpreadColumn::NONE;
2085 					break;
2086 			}
2087 			excels[iexcel].sheets[isheet].columns[col_index].type = type;
2088 			width /= 0xA;
2089 			if (width == 0) width = 8;
2090 			excels[iexcel].sheets[isheet].columns[col_index].width = width;
2091 
2092 			unsigned char c1 = cvehd[0x1E];
2093 			unsigned char c2 = cvehd[0x1F];
2094 			switch (c1) {
2095 				case 0x00: // Numeric	   - Dec1000
2096 				case 0x09: // Text&Numeric - Dec1000
2097 				case 0x10: // Numeric	   - Scientific
2098 				case 0x19: // Text&Numeric - Scientific
2099 				case 0x20: // Numeric	   - Engineering
2100 				case 0x29: // Text&Numeric - Engineering
2101 				case 0x30: // Numeric	   - Dec1,000
2102 				case 0x39: // Text&Numeric - Dec1,000
2103 					excels[iexcel].sheets[isheet].columns[col_index].valueType = (c1%0x10 == 0x9) ? TextNumeric : Numeric;
2104 					excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c1 / 0x10;
2105 					if (c2 >= 0x80) {
2106 						excels[iexcel].sheets[isheet].columns[col_index].significantDigits = c2 - 0x80;
2107 						excels[iexcel].sheets[isheet].columns[col_index].numericDisplayType = SignificantDigits;
2108 					} else if (c2 > 0) {
2109 						excels[iexcel].sheets[isheet].columns[col_index].decimalPlaces = c2 - 0x03;
2110 						excels[iexcel].sheets[isheet].columns[col_index].numericDisplayType = DecimalPlaces;
2111 					}
2112 					break;
2113 				case 0x02: // Time
2114 					excels[iexcel].sheets[isheet].columns[col_index].valueType = Time;
2115 					excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c2 - 0x80;
2116 					break;
2117 				case 0x03: // Date
2118 					excels[iexcel].sheets[isheet].columns[col_index].valueType = Date;
2119 					excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c2 - 0x80;
2120 					break;
2121 				case 0x31: // Text
2122 					excels[iexcel].sheets[isheet].columns[col_index].valueType = Text;
2123 					break;
2124 				case 0x04: // Month
2125 				case 0x34:
2126 					excels[iexcel].sheets[isheet].columns[col_index].valueType = Month;
2127 					excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c2;
2128 					break;
2129 				case 0x05: // Day
2130 				case 0x35:
2131 					excels[iexcel].sheets[isheet].columns[col_index].valueType = Day;
2132 					excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c2;
2133 					break;
2134 				default: // Text
2135 					excels[iexcel].sheets[isheet].columns[col_index].valueType = Text;
2136 					break;
2137 			}
2138 			if (cvedtsz > 0) {
2139 				excels[iexcel].sheets[isheet].columns[col_index].comment = cvedt.c_str();
2140 			}
2141 		}
2142 
2143 	} else {
2144 
2145 		GraphLayer& glayer = graphs[igraph].layers[ilayer];
2146 		glayer.curves.push_back(GraphCurve());
2147 		GraphCurve& curve(glayer.curves.back());
2148 
2149 		unsigned char h = cvehd[0x26];
2150 		curve.hidden = (h == 33);
2151 		curve.type = cvehd[0x4C];
2152 		if (curve.type == GraphCurve::XYZContour || curve.type == GraphCurve::Contour)
2153 			glayer.isXYY3D = false;
2154 
2155 		unsigned short w;
2156 		stmp.str(cvehd.substr(0x04));
2157 		GET_SHORT(stmp, w)
2158 		pair<string, string> column = findDataByIndex(w-1);
2159 		short nColY = w;
2160 		if (column.first.size() > 0) {
2161 			curve.dataName = column.first;
2162 			if (glayer.is3D() || (curve.type == GraphCurve::XYZContour)) {
2163 				curve.zColumnName = column.second;
2164 			} else {
2165 				curve.yColumnName = column.second;
2166 			}
2167 		}
2168 
2169 		stmp.str(cvehd.substr(0x23));
2170 		GET_SHORT(stmp, w)
2171 		column = findDataByIndex(w-1);
2172 		if (column.first.size() > 0) {
2173 			curve.xDataName = (curve.dataName != column.first) ? column.first : string();
2174 			if (glayer.is3D() || (curve.type == GraphCurve::XYZContour)) {
2175 				curve.yColumnName = column.second;
2176 			} else if (glayer.isXYY3D){
2177 				curve.xColumnName = column.second;
2178 			} else {
2179 				curve.xColumnName = column.second;
2180 			}
2181 		}
2182 
2183 		if (cvehdsz > 0x4E) {
2184 			stmp.str(cvehd.substr(0x4D));
2185 			GET_SHORT(stmp, w)
2186 			column = findDataByIndex(w-1);
2187 			if (column.first.size() > 0 && (glayer.is3D() || (curve.type == GraphCurve::XYZContour))) {
2188 				curve.xColumnName = column.second;
2189 				if (curve.dataName != column.first) {
2190 					// graph X and Y from different tables
2191 				}
2192 			}
2193 		}
2194 
2195 		if (glayer.is3D() || glayer.isXYY3D) graphs[igraph].is3D = true;
2196 
2197 		curve.lineConnect = cvehd[0x11];
2198 		curve.lineStyle = cvehd[0x12];
2199 		curve.boxWidth = cvehd[0x14];
2200 
2201 		stmp.str(cvehd.substr(0x15));
2202 		GET_SHORT(stmp, w)
2203 		curve.lineWidth = (double)w/500.0;
2204 
2205 		curve.symbolShape = cvehd[0x17];
2206 		curve.symbolInterior = cvehd[0x18];
2207 
2208 		stmp.str(cvehd.substr(0x19));
2209 		GET_SHORT(stmp, w)
2210 		curve.symbolSize = (double)w/500.0;
2211 
2212 		h = cvehd[0x1C];
2213 		curve.fillArea = (h==2);
2214 		curve.fillAreaType = cvehd[0x1E];
2215 
2216 		//text
2217 		if (curve.type == GraphCurve::TextPlot) {
2218 			stmp.str(cvehd.substr(0x13));
2219 			GET_SHORT(stmp, curve.text.rotation)
2220 			curve.text.rotation /= 10;
2221 			GET_SHORT(stmp, curve.text.fontSize)
2222 
2223 			h = cvehd[0x19];
2224 			switch (h) {
2225 				case 26:
2226 					curve.text.justify = TextProperties::Center;
2227 					break;
2228 				case 2:
2229 					curve.text.justify = TextProperties::Right;
2230 					break;
2231 				default:
2232 					curve.text.justify = TextProperties::Left;
2233 					break;
2234 			}
2235 
2236 			h = cvehd[0x20];
2237 			curve.text.fontUnderline = ((h & 0x1) != 0);
2238 			curve.text.fontItalic = ((h & 0x2) != 0);
2239 			curve.text.fontBold = ((h & 0x8) != 0);
2240 			curve.text.whiteOut = ((h & 0x20) != 0);
2241 
2242 			char offset = cvehd[0x37];
2243 			curve.text.xOffset = offset * 5;
2244 			offset = cvehd[0x38];
2245 			curve.text.yOffset = offset * 5;
2246 		}
2247 
2248 		//vector
2249 		if (curve.type == GraphCurve::FlowVector || curve.type == GraphCurve::Vector) {
2250 			stmp.str(cvehd.substr(0x56));
2251 			GET_FLOAT(stmp, curve.vector.multiplier)
2252 
2253 			h = cvehd[0x5E];
2254 			column = findDataByIndex(nColY - 1 + h - 0x64);
2255 			if (column.first.size() > 0)
2256 				curve.vector.endXColumnName = column.second;
2257 
2258 			h = cvehd[0x62];
2259 			column = findDataByIndex(nColY - 1 + h - 0x64);
2260 			if (column.first.size() > 0)
2261 				curve.vector.endYColumnName = column.second;
2262 
2263 			h = cvehd[0x18];
2264 			if (h >= 0x64) {
2265 				column = findDataByIndex(nColY - 1 + h - 0x64);
2266 				if (column.first.size() > 0)
2267 					curve.vector.angleColumnName = column.second;
2268 			} else if (h <= 0x08)
2269 				curve.vector.constAngle = 45*h;
2270 
2271 			h = cvehd[0x19];
2272 			if (h >= 0x64){
2273 				column = findDataByIndex(nColY - 1 + h - 0x64);
2274 				if (column.first.size() > 0)
2275 					curve.vector.magnitudeColumnName = column.second;
2276 			} else
2277 				curve.vector.constMagnitude = (int)curve.symbolSize;
2278 
2279 			stmp.str(cvehd.substr(0x66));
2280 			GET_SHORT(stmp, curve.vector.arrowLength)
2281 			curve.vector.arrowAngle = cvehd[0x68];
2282 
2283 			h = cvehd[0x69];
2284 			curve.vector.arrowClosed = !(h & 0x1);
2285 
2286 			stmp.str(cvehd.substr(0x70));
2287 			GET_SHORT(stmp, w)
2288 			curve.vector.width = (double)w/500.0;
2289 
2290 			h = cvehd[0x142];
2291 			switch (h) {
2292 				case 2:
2293 					curve.vector.position = VectorProperties::Midpoint;
2294 					break;
2295 				case 4:
2296 					curve.vector.position = VectorProperties::Head;
2297 					break;
2298 				default:
2299 					curve.vector.position = VectorProperties::Tail;
2300 					break;
2301 			}
2302 		}
2303 		//pie
2304 		if (curve.type == GraphCurve::Pie) {
2305 			// code from Origin410/500Parser
2306 			h = cvehd[0x14];
2307 			curve.pie.formatPercentages = ((h & 0x08) != 0);
2308 			curve.pie.formatValues = !curve.pie.formatPercentages;
2309 			curve.pie.positionAssociate = ((h & 0x80) != 0);
2310 			curve.pie.formatCategories  = ((h & 0x20) != 0);
2311 
2312 			h = cvehd[0x19];
2313 			curve.pie.radius = 100 - h;
2314 
2315 			h = cvehd[0x1A];
2316 			curve.pie.distance = h;
2317 			curve.pie.formatAutomatic = true;
2318 			curve.pie.viewAngle = 90;
2319 			curve.pie.thickness = 33;
2320 			curve.pie.rotation = 0;
2321 			curve.pie.horizontalOffset = 0;
2322 
2323 			if (cvehdsz > 0xA9) { // code from Origin750Parser.cpp
2324 
2325 				h = cvehd[0x92];
2326 				curve.pie.formatPercentages = ((h & 0x01) != 0);
2327 				curve.pie.formatValues = ((h & 0x02) != 0);
2328 				curve.pie.positionAssociate = ((h & 0x08) != 0);
2329 				curve.pie.clockwiseRotation = ((h & 0x20) != 0);
2330 				curve.pie.formatCategories = ((h & 0x80) != 0);
2331 
2332 				curve.pie.formatAutomatic = (cvehd[0x93] != 0);
2333 				stmp.str(cvehd.substr(0x94));
2334 				GET_SHORT(stmp, curve.pie.distance)
2335 				curve.pie.viewAngle = cvehd[0x96];
2336 				curve.pie.thickness = cvehd[0x98];
2337 
2338 				stmp.str(cvehd.substr(0x9A));
2339 				GET_SHORT(stmp, curve.pie.rotation)
2340 
2341 				stmp.str(cvehd.substr(0x9E));
2342 				GET_SHORT(stmp, curve.pie.displacement)
2343 
2344 				stmp.str(cvehd.substr(0xA0));
2345 				GET_SHORT(stmp, curve.pie.radius)
2346 				GET_SHORT(stmp, curve.pie.horizontalOffset)
2347 
2348 				stmp.str(cvehd.substr(0xA6));
2349 				GET_INT(stmp, curve.pie.displacedSectionCount)
2350 			}
2351 		}
2352 		//surface
2353 		if (glayer.isXYY3D || curve.type == GraphCurve::Mesh3D) {
2354 			curve.surface.type = cvehd[0x17];
2355 			h = cvehd[0x1C];
2356 			if ((h & 0x60) == 0x60)
2357 				curve.surface.grids = SurfaceProperties::X;
2358 			else if (h & 0x20)
2359 				curve.surface.grids = SurfaceProperties::Y;
2360 			else if (h & 0x40)
2361 				curve.surface.grids = SurfaceProperties::None;
2362 			else
2363 				curve.surface.grids = SurfaceProperties::XY;
2364 
2365 			curve.surface.sideWallEnabled = ((h & 0x10) != 0);
2366 			curve.surface.frontColor = getColor(cvehd.substr(0x1D,4));
2367 
2368 			h = cvehd[0x13];
2369 			curve.surface.backColorEnabled = ((h & 0x08) != 0);
2370 			curve.surface.surface.fill = ((h & 0x10) != 0);
2371 			curve.surface.surface.contour = ((h & 0x40) != 0);
2372 			curve.surface.topContour.fill = ((h & 0x02) != 0);
2373 			curve.surface.topContour.contour = ((h & 0x04) != 0);
2374 			curve.surface.bottomContour.fill = ((h & 0x80) != 0);
2375 			curve.surface.bottomContour.contour = ((h & 0x01) != 0);
2376 
2377 			if (cvehdsz > 0x165) {
2378 				stmp.str(cvehd.substr(0x14C));
2379 				GET_SHORT(stmp, w)
2380 				curve.surface.gridLineWidth = (double)w/500.0;
2381 				curve.surface.gridColor = getColor(cvehd.substr(0x14E,4));
2382 				curve.surface.backColor = getColor(cvehd.substr(0x15A,4));
2383 				curve.surface.xSideWallColor = getColor(cvehd.substr(0x15E,4));
2384 				curve.surface.ySideWallColor = getColor(cvehd.substr(0x162,4));
2385 			}
2386 			if (cvehdsz > 0xA9) {
2387 				stmp.str(cvehd.substr(0x94));
2388 				GET_SHORT(stmp, w)
2389 				curve.surface.surface.lineWidth = (double)w/500.0;
2390 				curve.surface.surface.lineColor = getColor(cvehd.substr(0x96,4));
2391 
2392 				stmp.str(cvehd.substr(0xB4));
2393 				GET_SHORT(stmp, w)
2394 				curve.surface.topContour.lineWidth = (double)w/500.0;
2395 				curve.surface.topContour.lineColor = getColor(cvehd.substr(0xB6,4));
2396 
2397 				stmp.str(cvehd.substr(0xA4));
2398 				GET_SHORT(stmp, w)
2399 				curve.surface.bottomContour.lineWidth = (double)w/500.0;
2400 				curve.surface.bottomContour.lineColor = getColor(cvehd.substr(0xA6,4));
2401 			}
2402 
2403 		}
2404 		if (curve.type == GraphCurve::Mesh3D || curve.type == GraphCurve::Contour || curve.type == GraphCurve::XYZContour) {
2405 			if (curve.type == GraphCurve::Contour || curve.type == GraphCurve::XYZContour) glayer.isXYY3D = false;
2406 			ColorMap& colorMap = (curve.type == GraphCurve::Mesh3D ? curve.surface.colorMap : curve.colorMap);
2407 			h = cvehd[0x13];
2408 			colorMap.fillEnabled = ((h & 0x82) != 0);
2409 
2410 			if ((curve.type == GraphCurve::Contour) && (cvehdsz > 0x89)) {
2411 				stmp.str(cvehd.substr(0x7A));
2412 				GET_SHORT(stmp, curve.text.fontSize)
2413 
2414 				h = cvehd[0x83];
2415 				curve.text.fontUnderline = ((h & 0x1) != 0);
2416 				curve.text.fontItalic = ((h & 0x2) != 0);
2417 				curve.text.fontBold = ((h & 0x8) != 0);
2418 				curve.text.whiteOut = ((h & 0x20) != 0);
2419 
2420 				curve.text.color = getColor(cvehd.substr(0x86,4));
2421 			}
2422 			if (cvedtsz > 0x6C) {
2423 				getColorMap(colorMap, cvedt, cvedtsz);
2424 			} else {
2425 				colorMap = glayer.colorMap;
2426 			}
2427 		}
2428 
2429 		if (fileVersion >= 850) {
2430 			curve.lineTransparency = cvehd[0x9C];
2431 			h = cvehd[0x9D];
2432 			curve.fillAreaWithLineTransparency = !h;
2433 			curve.fillAreaTransparency = cvehd[0x11E];
2434 		} else {
2435 			// use sensible default values
2436 			curve.fillAreaWithLineTransparency = false;
2437 			curve.fillAreaTransparency = 255;
2438 		}
2439 
2440 		if (cvehdsz > 0x143) {
2441 			curve.fillAreaColor = getColor(cvehd.substr(0xC2,4));
2442 			stmp.str(cvehd.substr(0xC6));
2443 			GET_SHORT(stmp, w)
2444 			curve.fillAreaPatternWidth = (double)w/500.0;
2445 
2446 			curve.fillAreaPatternColor = getColor(cvehd.substr(0xCA,4));
2447 			curve.fillAreaPattern = cvehd[0xCE];
2448 			curve.fillAreaPatternBorderStyle = cvehd[0xCF];
2449 			stmp.str(cvehd.substr(0xD0));
2450 			GET_SHORT(stmp, w)
2451 			curve.fillAreaPatternBorderWidth = (double)w/500.0;
2452 			curve.fillAreaPatternBorderColor = getColor(cvehd.substr(0xD2,4));
2453 
2454 			curve.fillAreaTransparency = cvehd[0x11E];
2455 
2456 			curve.lineColor = getColor(cvehd.substr(0x16A,4));
2457 
2458 			if (curve.type != GraphCurve::Contour) curve.text.color = curve.lineColor;
2459 
2460 			curve.symbolFillColor = getColor(cvehd.substr(0x12E,4));
2461 			curve.symbolColor = getColor(cvehd.substr(0x132,4));
2462 			curve.vector.color = curve.symbolColor;
2463 
2464 			h = cvehd[0x136];
2465 			curve.symbolThickness = (h == 255 ? 1 : h);
2466 			curve.pointOffset = cvehd[0x137];
2467 			h = cvehd[0x138];
2468 			curve.symbolFillTransparency = cvehd[0x139];
2469 
2470 			h = cvehd[0x143];
2471 			curve.connectSymbols = ((h & 0x8) != 0);
2472 		}
2473 	}
2474 }
2475 
getAxisBreakProperties(const string & abdata,unsigned int abdatasz)2476 void OriginAnyParser::getAxisBreakProperties(const string& abdata, unsigned int abdatasz) {
2477 	istringstream stmp;
2478 	(void) abdatasz;
2479 
2480 	if (ispread != -1) { // spreadsheet
2481 
2482 	} else if (imatrix != -1) { // matrix
2483 
2484 
2485 	} else if (iexcel != -1) { // excel
2486 
2487 
2488 	} else { // graph
2489 
2490 		GraphLayer& glayer = graphs[igraph].layers[ilayer];
2491 		unsigned char h = abdata[0x02];
2492 		if (h == 2) {
2493 			glayer.xAxisBreak.minorTicksBefore = glayer.xAxis.minorTicks;
2494 			glayer.xAxisBreak.scaleIncrementBefore = glayer.xAxis.step;
2495 			glayer.xAxisBreak.show = true;
2496 			stmp.str(abdata.substr(0x0B));
2497 			GET_DOUBLE(stmp, glayer.xAxisBreak.from)
2498 			GET_DOUBLE(stmp, glayer.xAxisBreak.to)
2499 			GET_DOUBLE(stmp, glayer.xAxisBreak.scaleIncrementAfter)
2500 			GET_DOUBLE(stmp, glayer.xAxisBreak.position)
2501 			h = abdata[0x2B];
2502 			glayer.xAxisBreak.log10 = (h == 1);
2503 			glayer.xAxisBreak.minorTicksAfter = abdata[0x2C];
2504 		} else if (h == 4) {
2505 			glayer.yAxisBreak.minorTicksBefore = glayer.yAxis.minorTicks;
2506 			glayer.yAxisBreak.scaleIncrementBefore = glayer.yAxis.step;
2507 			glayer.yAxisBreak.show = true;
2508 			stmp.str(abdata.substr(0x0B));
2509 			GET_DOUBLE(stmp, glayer.yAxisBreak.from)
2510 			GET_DOUBLE(stmp, glayer.yAxisBreak.to)
2511 			GET_DOUBLE(stmp, glayer.yAxisBreak.scaleIncrementAfter)
2512 			GET_DOUBLE(stmp, glayer.yAxisBreak.position)
2513 			h = abdata[0x2B];
2514 			glayer.yAxisBreak.log10 = (h == 1);
2515 			glayer.yAxisBreak.minorTicksAfter = abdata[0x2C];
2516 		}
2517 
2518 	}
2519 }
2520 
getAxisParameterProperties(const string & apdata,unsigned int apdatasz,int naxis)2521 void OriginAnyParser::getAxisParameterProperties(const string& apdata, unsigned int apdatasz, int naxis) {
2522 	istringstream stmp;
2523 	static int iaxispar = 0;
2524 
2525 	if (igraph != -1) {
2526 		unsigned char h = 0;
2527 		unsigned short w = 0;
2528 
2529 		GraphLayer& glayer = graphs[igraph].layers[ilayer];
2530 		GraphAxis axis = glayer.xAxis;
2531 		if (naxis == 1) {
2532 			axis = glayer.xAxis;
2533 		} else if (naxis == 2) {
2534 			axis = glayer.yAxis;
2535 		} else if (naxis == 3) {
2536 			axis = glayer.zAxis;
2537 		}
2538 		if (iaxispar == 0) { // minor Grid
2539 			h = apdata[0x26];
2540 			axis.minorGrid.hidden = (h==0);
2541 			axis.minorGrid.color = apdata[0x0F];
2542 			axis.minorGrid.style = apdata[0x12];
2543 			stmp.str(apdata.substr(0x15));
2544 			GET_SHORT(stmp, w)
2545 			axis.minorGrid.width = (double)w/500.0;
2546 		} else if (iaxispar == 1) { // major Grid
2547 			h = apdata[0x26];
2548 			axis.majorGrid.hidden = (h==0);
2549 			axis.majorGrid.color = apdata[0x0F];
2550 			axis.majorGrid.style = apdata[0x12];
2551 			stmp.str(apdata.substr(0x15));
2552 			GET_SHORT(stmp, w)
2553 			axis.majorGrid.width = (double)w/500.0;
2554 		} else if (iaxispar == 2) { // tickaxis 0
2555 			h = apdata[0x26];
2556 			axis.tickAxis[0].showMajorLabels = ((h & 0x40) != 0);
2557 			axis.tickAxis[0].color = apdata[0x0F];
2558 			stmp.str(apdata.substr(0x13));
2559 			GET_SHORT(stmp, w)
2560 			axis.tickAxis[0].rotation = w/10;
2561 			GET_SHORT(stmp, w)
2562 			axis.tickAxis[0].fontSize = w;
2563 			h = apdata[0x1A];
2564 			axis.tickAxis[0].fontBold = ((h & 0x08) != 0);
2565 			stmp.str(apdata.substr(0x23));
2566 			GET_SHORT(stmp, w)
2567 			h = apdata[0x25];
2568 			unsigned char h1 = apdata[0x26];
2569 			axis.tickAxis[0].valueType = (ValueType)(h & 0x0F);
2570 			pair<string, string> column;
2571 			switch (axis.tickAxis[0].valueType) {
2572 				case Numeric:
2573 					/*switch ((h>>4)) {
2574 						case 0x9:
2575 							axis.tickAxis[0].valueTypeSpecification=1;
2576 							break;
2577 						case 0xA:
2578 							axis.tickAxis[0].valueTypeSpecification=2;
2579 							break;
2580 						case 0xB:
2581 							axis.tickAxis[0].valueTypeSpecification=3;
2582 							break;
2583 						default:
2584 							axis.tickAxis[0].valueTypeSpecification=0;
2585 					}*/
2586 					if ((h>>4) > 7) {
2587 						axis.tickAxis[0].valueTypeSpecification = (h>>4) - 8;
2588 						axis.tickAxis[0].decimalPlaces = h1 - 0x40;
2589 					} else {
2590 						axis.tickAxis[0].valueTypeSpecification = (h>>4);
2591 						axis.tickAxis[0].decimalPlaces = -1;
2592 					}
2593 					break;
2594 				case Time:
2595 				case Date:
2596 				case Month:
2597 				case Day:
2598 				case ColumnHeading:
2599 					axis.tickAxis[0].valueTypeSpecification = h1 - 0x40;
2600 					break;
2601 				case Text:
2602 				case TickIndexedDataset:
2603 				case Categorical:
2604 					column = findDataByIndex(w-1);
2605 					if (column.first.size() > 0) {
2606 						axis.tickAxis[0].dataName = column.first;
2607 						axis.tickAxis[0].columnName = column.second;
2608 					}
2609 					break;
2610 				case TextNumeric: // Numeric Decimal 1.000
2611 					axis.tickAxis[0].valueType = Numeric;
2612 					axis.tickAxis[0].valueTypeSpecification = 0;
2613 					break;
2614 			}
2615 		} else if (iaxispar == 3) { // formataxis 0
2616 			h = apdata[0x26];
2617 			axis.formatAxis[0].hidden = (h == 0);
2618 			axis.formatAxis[0].color = apdata[0x0F];
2619 			if (apdatasz > 0x4B) {
2620 				stmp.str(apdata.substr(0x4A));
2621 				GET_SHORT(stmp, w)
2622 				axis.formatAxis[0].majorTickLength = (double)w/10.0;
2623 			}
2624 			stmp.str(apdata.substr(0x15));
2625 			GET_SHORT(stmp, w)
2626 			axis.formatAxis[0].thickness = (double)w/500.0;
2627 			h = apdata[0x25];
2628 			axis.formatAxis[0].minorTicksType = (h>>6);
2629 			axis.formatAxis[0].majorTicksType = ((h>>4) & 3);
2630 			axis.formatAxis[0].axisPosition = (h & 0x0F);
2631 			short w1 = 0;
2632 			switch (axis.formatAxis[0].axisPosition) { // TODO: check if correct
2633 				case 1:
2634 					stmp.str(apdata.substr(0x37));
2635 					GET_SHORT(stmp, w1)
2636 					axis.formatAxis[0].axisPositionValue = (double)w1;
2637 					break;
2638 				case 2:
2639 					stmp.str(apdata.substr(0x2F));
2640 					GET_DOUBLE(stmp, axis.formatAxis[0].axisPositionValue)
2641 					break;
2642 			}
2643 		} else if (iaxispar == 4) { // tickaxis 1
2644 			h = apdata[0x26];
2645 			axis.tickAxis[1].showMajorLabels = ((h & 0x40) != 0);
2646 			axis.tickAxis[1].color = apdata[0x0F];
2647 			stmp.str(apdata.substr(0x13));
2648 			GET_SHORT(stmp, w)
2649 			axis.tickAxis[1].rotation = w/10;
2650 			GET_SHORT(stmp, w)
2651 			axis.tickAxis[1].fontSize = w;
2652 			h = apdata[0x1A];
2653 			axis.tickAxis[1].fontBold = ((h & 0x08) != 0);
2654 			stmp.str(apdata.substr(0x23));
2655 			GET_SHORT(stmp, w)
2656 			h = apdata[0x25];
2657 			unsigned char h1 = apdata[0x26];
2658 			axis.tickAxis[1].valueType = (ValueType)(h & 0x0F);
2659 			pair<string, string> column;
2660 			switch (axis.tickAxis[1].valueType) {
2661 				case Numeric:
2662 					/*switch ((h>>4)) {
2663 						case 0x9:
2664 							axis.tickAxis[1].valueTypeSpecification=1;
2665 							break;
2666 						case 0xA:
2667 							axis.tickAxis[1].valueTypeSpecification=2;
2668 							break;
2669 						case 0xB:
2670 							axis.tickAxis[1].valueTypeSpecification=3;
2671 							break;
2672 						default:
2673 							axis.tickAxis[1].valueTypeSpecification=0;
2674 					}*/
2675 					if ((h>>4) > 7) {
2676 						axis.tickAxis[1].valueTypeSpecification = (h>>4) - 8;
2677 						axis.tickAxis[1].decimalPlaces = h1 - 0x40;
2678 					} else {
2679 						axis.tickAxis[1].valueTypeSpecification = (h>>4);
2680 						axis.tickAxis[1].decimalPlaces = -1;
2681 					}
2682 					break;
2683 				case Time:
2684 				case Date:
2685 				case Month:
2686 				case Day:
2687 				case ColumnHeading:
2688 					axis.tickAxis[1].valueTypeSpecification = h1 - 0x40;
2689 					break;
2690 				case Text:
2691 				case TickIndexedDataset:
2692 				case Categorical:
2693 					column = findDataByIndex(w-1);
2694 					if (column.first.size() > 0) {
2695 						axis.tickAxis[1].dataName = column.first;
2696 						axis.tickAxis[1].columnName = column.second;
2697 					}
2698 					break;
2699 				case TextNumeric: // Numeric Decimal 1.000
2700 					axis.tickAxis[1].valueType = Numeric;
2701 					axis.tickAxis[1].valueTypeSpecification = 0;
2702 				break;
2703 			}
2704 		} else if (iaxispar == 5) { // formataxis 1
2705 			h = apdata[0x26];
2706 			axis.formatAxis[1].hidden = (h == 0);
2707 			axis.formatAxis[1].color = apdata[0x0F];
2708 			if (apdatasz > 0x4B) {
2709 				stmp.str(apdata.substr(0x4A));
2710 				GET_SHORT(stmp, w)
2711 				axis.formatAxis[1].majorTickLength = (double)w/10.0;
2712 			}
2713 			stmp.str(apdata.substr(0x15));
2714 			GET_SHORT(stmp, w)
2715 			axis.formatAxis[1].thickness = (double)w/500.0;
2716 			h = apdata[0x25];
2717 			axis.formatAxis[1].minorTicksType = (h>>6);
2718 			axis.formatAxis[1].majorTicksType = ((h>>4) & 3);
2719 			axis.formatAxis[1].axisPosition = (h & 0x0F);
2720 			short w1 = 0;
2721 			switch (axis.formatAxis[1].axisPosition) { // TODO: check if correct
2722 				case 1:
2723 					stmp.str(apdata.substr(0x37));
2724 					GET_SHORT(stmp, w1)
2725 					axis.formatAxis[1].axisPositionValue = (double)w1;
2726 					break;
2727 				case 2:
2728 					stmp.str(apdata.substr(0x2F));
2729 					GET_DOUBLE(stmp, axis.formatAxis[1].axisPositionValue)
2730 					break;
2731 			}
2732 		}
2733 
2734 		if (naxis == 1) {
2735 			glayer.xAxis = axis;
2736 		} else if (naxis == 2) {
2737 			glayer.yAxis = axis;
2738 		} else if (naxis == 3) {
2739 			glayer.zAxis = axis;
2740 		}
2741 
2742 		iaxispar++;
2743 		iaxispar %= 6;
2744 
2745 	}
2746 }
2747 
getNoteProperties(const string & nwehd,unsigned int nwehdsz,const string & nwelb,unsigned int nwelbsz,const string & nwect,unsigned int nwectsz)2748 void OriginAnyParser::getNoteProperties(const string& nwehd, unsigned int nwehdsz, const string& nwelb, unsigned int nwelbsz, const string& nwect, unsigned int nwectsz) {
2749 	LOG_PRINT(logfile, "OriginAnyParser::getNoteProperties()");
2750 	istringstream stmp;
2751 	(void) nwehdsz; (void) nwelbsz; (void) nwectsz;
2752 
2753 	// note window position and size
2754 	Rect rect;
2755 	unsigned int coord;
2756 	stmp.str(nwehd);
2757 	GET_INT(stmp, coord)
2758 	rect.left = static_cast<short>(coord);
2759 	GET_INT(stmp, coord)
2760 	rect.top = static_cast<short>(coord);
2761 	GET_INT(stmp, coord)
2762 	rect.right = static_cast<short>(coord);
2763 	GET_INT(stmp, coord)
2764 	rect.bottom = static_cast<short>(coord);
2765 
2766 	string name = nwelb.c_str();
2767 
2768 	// ResultsLog note window has left, top, right, bottom all zero.
2769 	// All other parameters are also zero, except "name" and "text".
2770 	if (!rect.bottom || !rect.right) {
2771 		resultsLog = nwect;
2772 		return;
2773 	}
2774 	unsigned char state = nwehd[0x18];
2775 
2776 	// files from version < 6.0 have nwehdsz < 1D
2777 	if (nwehdsz < 0x2F) return;
2778 
2779 	double creationDate, modificationDate;
2780 	stmp.str(nwehd.substr(0x20));
2781 	GET_DOUBLE(stmp, creationDate)
2782 	GET_DOUBLE(stmp, modificationDate)
2783 
2784 	if (nwehdsz < 0x38) return;
2785 	unsigned char c = nwehd[0x38];
2786 
2787 	if (nwehdsz < 0x3F) return;
2788 	unsigned int labellen = 0;
2789 	stmp.str(nwehd.substr(0x3C));
2790 	GET_INT(stmp, labellen)
2791 
2792 	notes.push_back(Note(name));
2793 	LOG_PRINT(logfile,"notes: %d\n", (int)notes.size());
2794 	notes.back().objectID = objectIndex;
2795 	++objectIndex;
2796 
2797 	notes.back().frameRect = rect;
2798 	notes.back().creationDate = doubleToPosixTime(creationDate);
2799 	notes.back().modificationDate = doubleToPosixTime(modificationDate);
2800 
2801 	if (c == 0x01)
2802 		notes.back().title = Window::Label;
2803 	else if (c == 0x02)
2804 		notes.back().title = Window::Name;
2805 	else
2806 		notes.back().title = Window::Both;
2807 
2808 	if (state == 0x07)
2809 		notes.back().state = Window::Minimized;
2810 	else if (state == 0x0b)
2811 		notes.back().state = Window::Maximized;
2812 
2813 	notes.back().hidden = ((state & 0x40) != 0);
2814 
2815 	if (labellen > 1) {
2816 		notes.back().label = nwect.substr(0,labellen);
2817 		notes.back().text = nwect.substr(labellen).c_str();
2818 	} else {
2819 		notes.back().text = nwect.c_str();
2820 	}
2821 }
2822 
getColorMap(ColorMap & cmap,const string & cmapdata,unsigned int cmapdatasz)2823 void OriginAnyParser::getColorMap(ColorMap& cmap, const string& cmapdata, unsigned int cmapdatasz) {
2824 	istringstream stmp;
2825 	unsigned int cmoffset = 0;
2826 	// color maps for matrix annotations have a different offset than graph curve's colormaps
2827 	if (imatrix != -1) {
2828 		cmoffset = 0x14;
2829 	} else if (igraph != -1) {
2830 		cmoffset = 0x6C;
2831 	} else {
2832 		return;
2833 	}
2834 
2835 	stmp.str(cmapdata.substr(cmoffset));
2836 	unsigned int colorMapSize = 0;
2837 	GET_INT(stmp, colorMapSize)
2838 
2839 	// check we have enough data to fill the map
2840 	unsigned int minDataSize = cmoffset + 0x114 + (colorMapSize+2)*0x38;
2841 	if (minDataSize > cmapdatasz) {
2842 		LOG_PRINT(logfile, "WARNING: Too few data while getting ColorMap. Needed: at least %d bytes. Have: %d bytes.\n", minDataSize, cmapdatasz)
2843 		return;
2844 	}
2845 
2846 	unsigned int lvl_offset = 0;
2847 	for (unsigned int i = 0; i < colorMapSize + 3; ++i) {
2848 		lvl_offset = cmoffset + 0x114 + i*0x38;
2849 		ColorMapLevel level;
2850 
2851 		level.fillPattern = cmapdata[lvl_offset];
2852 		level.fillPatternColor = getColor(cmapdata.substr(lvl_offset+0x04, 4));
2853 
2854 		stmp.str(cmapdata.substr(lvl_offset+0x08));
2855 		unsigned short w;
2856 		GET_SHORT(stmp, w)
2857 		level.fillPatternLineWidth = (double)w/500.0;
2858 
2859 		level.lineStyle = cmapdata[lvl_offset+0x10];
2860 
2861 		stmp.str(cmapdata.substr(lvl_offset+0x12));
2862 		GET_SHORT(stmp, w)
2863 		level.lineWidth = (double)w/500.0;
2864 
2865 		level.lineColor = getColor(cmapdata.substr(lvl_offset+0x14, 4));
2866 
2867 		unsigned char h = cmapdata[lvl_offset+0x1A];
2868 		level.labelVisible = (h & 0x1);
2869 		level.lineVisible = !(h & 0x2);
2870 
2871 		level.fillColor = getColor(cmapdata.substr(lvl_offset+0x28, 4));
2872 
2873 		double value = 0.0;
2874 		stmp.str(cmapdata.substr(lvl_offset+0x30));
2875 		GET_DOUBLE(stmp, value)
2876 
2877 		cmap.levels.push_back(make_pair(value, level));
2878 	}
2879 
2880 }
2881 
getZcolorsMap(ColorMap & colorMap,const string & cmapdata,unsigned int cmapdatasz)2882 void OriginAnyParser::getZcolorsMap(ColorMap& colorMap, const string& cmapdata, unsigned int cmapdatasz) {
2883 	istringstream stmp;
2884 	(void) cmapdatasz;
2885 
2886 	Color lowColor;//color below
2887 	lowColor.type = Origin::Color::Custom;
2888 	lowColor.custom[0] = cmapdata[0x0E];
2889 	lowColor.custom[1] = cmapdata[0x0F];
2890 	lowColor.custom[2] = cmapdata[0x10];
2891 	// skip an unsigned char at 0x11
2892 
2893 	Color highColor;//color above
2894 	highColor.type = Origin::Color::Custom;
2895 	highColor.custom[0] = cmapdata[0x12];
2896 	highColor.custom[1] = cmapdata[0x13];
2897 	highColor.custom[2] = cmapdata[0x14];
2898 	// skip an unsigned char at 0x15
2899 
2900 	unsigned short colorMapSize;
2901 	stmp.str(cmapdata.substr(0x16));
2902 	GET_SHORT(stmp, colorMapSize)
2903 	// skip a short at 0x18-0x19
2904 
2905 	for (int i = 0; i < 4; ++i) {//low, high, middle and missing data colors
2906 		Color color; (void) color;
2907 		color.type = Origin::Color::Custom;
2908 		color.custom[0] = cmapdata[0x1A+4*i];
2909 		color.custom[1] = cmapdata[0x1B+4*i];
2910 		color.custom[2] = cmapdata[0x1C+4*i];
2911 	}
2912 
2913 	double zmin, zmax, zmissing;
2914 	stmp.str(cmapdata.substr(0x2A));
2915 	GET_DOUBLE(stmp, zmin);
2916 	GET_DOUBLE(stmp, zmax);
2917 	GET_DOUBLE(stmp, zmissing);
2918 
2919 	short val;
2920 	for (int i = 0; i < 2; ++i) {
2921 		Color color; (void) color;
2922 		color.type = Origin::Color::Custom;
2923 		color.custom[0] = cmapdata[0x66+10*i];
2924 		color.custom[1] = cmapdata[0x67+10*i];
2925 		color.custom[2] = cmapdata[0x68+10*i];
2926 		// skip an unsigned char at 0x69+10*i
2927 		stmp.str(cmapdata.substr(0x6A+10*i));
2928 		GET_SHORT(stmp, val)
2929 	}
2930 
2931 	ColorMapLevel level;
2932 	level.fillColor = lowColor;
2933 	colorMap.levels.push_back(make_pair(zmin, level));
2934 
2935 	for (int i = 0; i < (colorMapSize + 1); ++i) {
2936 		Color color; (void) color;
2937 		color.type = Origin::Color::Custom;
2938 		color.custom[0] = cmapdata[0x7A+10*i];
2939 		color.custom[1] = cmapdata[0x7B+10*i];
2940 		color.custom[2] = cmapdata[0x7C+10*i];
2941 		// skip an unsigned char at 0x7D+10*i
2942 		stmp.str(cmapdata.substr((0x7E)+10*i));
2943 		GET_SHORT(stmp, val)
2944 
2945 		level.fillColor = color;
2946 		colorMap.levels.push_back(make_pair(val, level));
2947 	}
2948 
2949 	level.fillColor = highColor;
2950 	colorMap.levels.push_back(make_pair(zmax, level));
2951 }
2952 
getProjectLeafProperties(tree<ProjectNode>::iterator current_folder,const string & ptldt,unsigned int ptldtsz)2953 void OriginAnyParser::getProjectLeafProperties(tree<ProjectNode>::iterator current_folder, const string& ptldt, unsigned int ptldtsz) {
2954 	LOG_PRINT(logfile,"OriginAnyParser::getProjectLeafProperties()\n");
2955 	istringstream stmp;
2956 	(void) ptldtsz;
2957 
2958 	stmp.str(ptldt);
2959 	unsigned int file_type = 0, file_object_id = 0;
2960 	GET_INT(stmp, file_type);
2961 	GET_INT(stmp, file_object_id);
2962 
2963 	LOG_PRINT(logfile,"file_type=%d file_object_id=%d\n",file_type,file_object_id);
2964 	if (file_type == 0x100000) { // Note window
2965 		LOG_PRINT(logfile,"notes.size()=%d\n",(int)notes.size());
2966 		if ((file_object_id <= notes.size()) && (notes.size()>0)) {
2967 			projectTree.append_child(current_folder, ProjectNode(notes[file_object_id].name, ProjectNode::Note));
2968 		}
2969 	} else { // other windows
2970 		tree<Origin::ProjectNode>::iterator childnode;
2971 		pair<ProjectNode::NodeType, Origin::Window> object = findWindowObjectByIndex(file_object_id);
2972 		childnode=projectTree.append_child(current_folder, ProjectNode(object.second.name, object.first));
2973 		(*childnode).creationDate = object.second.creationDate;
2974 		(*childnode).modificationDate = object.second.modificationDate;
2975 	}
2976 }
2977 
getProjectFolderProperties(tree<ProjectNode>::iterator current_folder,const string & flehd,unsigned int flehdsz)2978 void OriginAnyParser::getProjectFolderProperties(tree<ProjectNode>::iterator current_folder, const string& flehd, unsigned int flehdsz) {
2979 	istringstream stmp;
2980 	(void) flehdsz;
2981 
2982 	unsigned char a = flehd[0x02];
2983 	(*current_folder).active = (a == 1);
2984 
2985 	double creationDate, modificationDate;
2986 	stmp.str(flehd.substr(0x10));
2987 	GET_DOUBLE(stmp, creationDate);
2988 	GET_DOUBLE(stmp, modificationDate);
2989 
2990 	(*current_folder).creationDate = doubleToPosixTime(creationDate);
2991 	(*current_folder).modificationDate = doubleToPosixTime(modificationDate);
2992 }
2993 
outputProjectTree(std::ostream & out)2994 void OriginAnyParser::outputProjectTree(std::ostream & out) {
2995 	size_t windowsCount = spreadSheets.size()+matrixes.size()+excels.size()+graphs.size()+notes.size();
2996 
2997 	out << "Project has " << windowsCount << " windows." << endl;
2998 	out << "Origin project Tree" << endl;
2999 
3000 	char cdsz[21];
3001 	for (tree<ProjectNode>::iterator it = projectTree.begin(projectTree.begin()); it != projectTree.end(projectTree.begin()); ++it) {
3002 		strftime(cdsz, sizeof(cdsz), "%F %T", gmtime(&(*it).creationDate));
3003 		out <<  string(projectTree.depth(it) - 1, ' ') <<  (*it).name.c_str() << "\t" << cdsz << endl;
3004 	}
3005 }
3006