1 /*	The code in this file writes a sample Igor Pro packed experiment file.
2 
3 	See Igor Pro Tech Note PTN#003 for details.
4 */
5 
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <stddef.h>					// For offsetof macro.
10 
11 #include "PackedFile.h"
12 #include "Variables.h"
13 #include "IgorBin.h"
14 #include "CrossPlatformFileIO.h"
15 #include "IgorSupport.h"
16 
17 #include "PackedFileDemo.h"
18 
19 // Constants
20 #define NOMEM 1						// Error code for out of memory.
21 #define DEMO_WAVE_LENGTH 10			// Number of points in the demo wave.
22 
23 static void
PrintResult(const char * message,int error)24 PrintResult(const char* message, int error)
25 {
26 	char buffer[256];
27 
28 	if (error)
29 		sprintf(buffer, "Error %d: %s", error, message);
30 	else
31 		strcpy(buffer, message);
32 	printf("%s\n", buffer);
33 }
34 
35 static int
WriteDemoVariablesRecord(CP_FILE_REF fr)36 WriteDemoVariablesRecord(CP_FILE_REF fr)
37 {
38 	unsigned long recordStartPos;
39 	char temp[256];
40 	int err;
41 
42 	if (err = CPGetFilePosition(fr, &recordStartPos))		// Remember where record starts in the file.
43 		return err;
44 
45 	// Write the generic packed file record header. numDataBytes field will be updated later.
46 	if (err = WriteRecordHeader(fr, kVariablesRecord, 0, 0))
47 		return err;
48 
49 	// Write the specific header for variables data.
50 	if (err = WriteVariablesHeader(fr, 0, 1, 1, 0, 0))	// There will be one numeric and one string variable.
51 		return err;
52 
53 	// Write the numeric variable.
54 	if (err = WriteNumericVar(fr, "userNumVar", 0, 1234.0, 0.0))
55 		return err;
56 
57 	// Write the string variable.
58 	strcpy(temp, "This is a string variable.");
59 	if (err = WriteStringVar(fr, "userStrVar", temp, strlen(temp)))
60 		return err;
61 
62 	// Update the numDataBytes field to record the actual length of the data.
63 	if (err = SetRecordHeaderNumDataBytesField(fr, recordStartPos))
64 		return err;
65 
66 	return 0;
67 }
68 
69 static void
SetDemoWaveDataContents(float * fp,long numPoints)70 SetDemoWaveDataContents(float* fp, long numPoints)
71 {
72 	float val;
73 
74 	val = 0.0;
75 	while(numPoints > 0) {
76 		*fp++ = val;
77 		val += 1.0;
78 		numPoints -= 1;
79 	}
80 }
81 
82 static int
WriteDemoWave1DNumericWaveRecord(CP_FILE_REF fr)83 WriteDemoWave1DNumericWaveRecord(CP_FILE_REF fr)
84 {
85 	WaveHeader2 wh;
86 	float* data;
87 	unsigned long recordStartPos;
88 	unsigned long creationDate;
89 	long waveMainDataSize, noteSize;
90 	char note[256];
91 	int err;
92 
93 	if (err = CPGetFilePosition(fr, &recordStartPos))		// Remember where record starts in the file.
94 		return err;
95 
96 	// Write the generic packed file record header. numDataBytes field will be updated later.
97 	if (err = WriteRecordHeader(fr, kWaveRecord, 0, 0))
98 		return err;
99 
100 	// It is OK to pass 0 for creationDate if you don't care about it.
101 	#ifdef WIN32
102 		creationDate = 0;						// GetDateTime can be simulated on Windows but it is fairly complicated.
103 	#else
104 		GetDateTime(&creationDate);				// Seconds since midnight, January 1, 1904.
105 	#endif
106 
107 	// Create the demo data.
108 	waveMainDataSize = DEMO_WAVE_LENGTH * sizeof(float);		// Size of just the numeric data.
109 	data = malloc(waveMainDataSize);
110 	if (data == NULL)
111 		return NOMEM;
112 	SetDemoWaveDataContents(data, DEMO_WAVE_LENGTH);
113 
114 	// This sets structure to be valid for a 1D numeric wave.
115 	InitWaveHeader2(&wh, "wave0", creationDate, DEMO_WAVE_LENGTH, NT_FP32);
116 
117 	// Set X scaling. x = hsA*p + hsB.
118 	wh.hsA = 0.2;
119 	wh.hsB = 0.0;
120 
121 	/*	For the checksum calculated by WriteVersion2NumericWave, the wData field of the
122 		WaveHeader2 structure must contain the same data as the first 16 bytes of the main
123 		wave data. The wData field was cleared by InitWaveHeader2.
124 	*/
125 	{
126 		int tmp = 16;
127 		if (waveMainDataSize < tmp)
128 			tmp = waveMainDataSize;
129 		memcpy(&wh.wData, data, tmp);
130 	}
131 
132 	// We need to know the size of the wave note.
133 	strcpy(note, "This is wave0.");
134 	noteSize = strlen(note);
135 
136 	// Write the wave.
137 	err = WriteVersion2NumericWave(fr, &wh, data, note, strlen(note));
138 	free(data);
139 	if (err)
140 		return err;
141 
142 	// Update the numDataBytes field to record the actual length of the data.
143 	if (err = SetRecordHeaderNumDataBytesField(fr, recordStartPos))
144 		return err;
145 
146 	return 0;
147 }
148 
149 static int
WriteDemoRecreationProcedures(CP_FILE_REF fr)150 WriteDemoRecreationProcedures(CP_FILE_REF fr)
151 {
152 	char text[1024];
153 
154 	// Generate text to call the Table0() macro defined below.
155 	strcpy(text, "Table0()"CR_STR);
156 	strcat(text, CR_STR);
157 
158 	// Generate text for a table recreation macro to display wave0.
159 	strcat(text, "Window Table0() : Table"CR_STR);
160 	strcat(text, "\tPauseUpdate; Silent 1		| building window..."CR_STR);
161 	strcat(text, "\tEdit wave0"CR_STR);
162 	strcat(text, "End"CR_STR);
163 
164 	return WritePlainTextRecord(fr, kRecreationRecord, text, strlen(text));
165 }
166 
167 static int
WriteDemoProcedureWindowProcedures(CP_FILE_REF fr)168 WriteDemoProcedureWindowProcedures(CP_FILE_REF fr)
169 {
170 	char text[1024];
171 
172 	// Generate procedure window text.
173 	strcpy(text, "Macro Demo()"CR_STR);
174 	strcat(text, "\tDoAlert 0, \"Greetings!\""CR_STR);
175 	strcat(text, "End"CR_STR);
176 
177 	return WritePlainTextRecord(fr, kProcedureRecord, text, strlen(text));
178 }
179 
180 static int
WriteDemoHistory(CP_FILE_REF fr)181 WriteDemoHistory(CP_FILE_REF fr)
182 {
183 	char text[1024];
184 	int err;
185 
186 	// Generate history text.
187 	strcpy(text, "// This experiment was created using Igor Pro Tech Note PTN003"CR_STR);
188 	strcat(text, "// This tech note shows how to write a packed Igor Pro 3.0 file from your own program."CR_STR);
189 
190 	if (err = WritePlainTextRecord(fr, kHistoryRecord, text, strlen(text)))
191 		return err;
192 	return WriteRecordHeader(fr, kGetHistoryRecord, 0, 0);
193 }
194 
195 static int
WriteDemoPackedFile(CP_FILE_REF fr)196 WriteDemoPackedFile(CP_FILE_REF fr)
197 {
198 	int err;
199 
200 	if (err = WriteDemoVariablesRecord(fr))
201 		return err;
202 	if (err = WriteDemoWave1DNumericWaveRecord(fr))
203 		return err;
204 	if (err = WriteDemoRecreationProcedures(fr))
205 		return err;
206 	if (err = WriteDemoProcedureWindowProcedures(fr))
207 		return err;
208 	if (err = WriteDemoHistory(fr))
209 		return err;
210 	return 0;
211 }
212 
213 int
WritePackedFile(const char * packedFilePath)214 WritePackedFile(const char* packedFilePath)
215 {
216 	CP_FILE_REF fr;			// Ref number for the file we are writing to.
217 	char temp[256];
218 	int err;
219 
220 	// Delete test file if it already exists and create a new file.
221 	if (err = CPCreateFile(packedFilePath, 1, IGOR_CREATOR_CODE, IGOR_PACKED_FILE_TYPE)) {
222 		PrintResult("Create file error", err);
223 		return err;
224 	}
225 
226 	if (err = CPOpenFile(packedFilePath, 1, &fr)) {
227 		PrintResult("Open file error", err);
228 		return err;
229 	}
230 
231 	// Write all of the records to the file.
232 	if (err = WriteDemoPackedFile(fr)) {
233 		PrintResult("WritePackedFile error", err);
234 		return err;
235 	}
236 
237 	if (err = CPCloseFile(fr)) {
238 		PrintResult("Close file error", err);
239 		return err;
240 	}
241 
242 	sprintf(temp, "\"%s\" written successfully.", packedFilePath);
243 	PrintResult(temp, 0);
244 
245 	return 0;
246 }
247