lots of work around generated binary data files
Haldean Brown
3 years ago
0 | 0 | cxx_sources = \ |
1 | 1 | src/ana.cpp \ |
2 | src/font.gen.cpp \ | |
2 | 3 | src/node.cpp \ |
3 | 4 | src/reallocarray.cpp \ |
4 | 5 | src/shaders.cpp \ |
5 | 6 | src/ui.cpp \ |
7 | src/viewport.cpp \ | |
6 | 8 | lib/imgui/imgui.cpp \ |
7 | 9 | lib/imgui/imgui_draw.cpp \ |
8 | 10 | lib/imgui/imgui_widgets.cpp \ |
36 | 38 | mkdir -p build |
37 | 39 | $(CXX) $(objects) $(LDFLAGS) -o $@ |
38 | 40 | |
41 | XD = tool/xd.exe | |
42 | ||
43 | $(XD): tool/xd-1.4/xd.o | |
44 | $(CXX) $< -o $@ | |
45 | ||
46 | src/font.gen.cpp: data/InputMonoNarrow-Regular.ttf $(XD) | |
47 | $(XD) -dfont_data $< $@ | |
48 | ||
39 | 49 | -include $(patsubst %.o,%.d,$(objects)) |
40 | 50 | |
41 | 51 | clean: |
42 | rm -rf $(objects) build | |
52 | rm -rf $(objects) build tool/xd-1.4/xd.o tool/xd.exe | |
43 | 53 | .PHONY: clean |
0 | /* | |
1 | ||
2 | Extended dump and load utility | |
3 | ||
4 | by John Walker | |
5 | http://www.fourmilab.ch/ | |
6 | ||
7 | This program is in the public domain. | |
8 | ||
9 | */ | |
10 | ||
11 | #define Version \ | |
12 | "1.4 -- October 2017" | |
13 | ||
14 | ||
15 | #include <stdio.h> | |
16 | #include <ctype.h> | |
17 | #include <string.h> | |
18 | #ifdef _WIN32 | |
19 | #include <fcntl.h> | |
20 | #include <io.h> | |
21 | #endif | |
22 | ||
23 | #define FALSE 0 | |
24 | #define TRUE 1 | |
25 | ||
26 | #define EOS '\0' | |
27 | ||
28 | static char addrformat[80] = "%6X"; | |
29 | static char scanaddr[80] = "%lx%c"; | |
30 | ||
31 | static char dataformat1[80] = "%02X"; | |
32 | static char scandata[80] = "%x%n%c"; | |
33 | ||
34 | static int bytesperline = 16, doublechar = FALSE, | |
35 | dflen = 2, loading = FALSE, streaming = FALSE; | |
36 | static unsigned long fileaddr; | |
37 | static unsigned char lineecho[32]; | |
38 | ||
39 | /* OUTLINE -- Edit a line of binary data into the selected output | |
40 | format. */ | |
41 | ||
42 | static void outline(out, dat, len) | |
43 | FILE *out; | |
44 | unsigned char *dat; | |
45 | int len; | |
46 | { | |
47 | char oline[132]; | |
48 | int i; | |
49 | ||
50 | sprintf(oline, addrformat, fileaddr); | |
51 | strcat(oline, ":"); | |
52 | for (i = 0; i < len; i++) { | |
53 | char outedit[80]; | |
54 | ||
55 | sprintf(outedit, dataformat1, dat[i]); | |
56 | strcat(oline, (i == (bytesperline / 2)) ? " " : " "); | |
57 | strcat(oline, outedit); | |
58 | } | |
59 | ||
60 | if (doublechar) { | |
61 | char oc[2]; | |
62 | int shortfall = ((bytesperline - len) * (dflen + 1)) + | |
63 | (len <= (bytesperline / 2) ? 1 : 0); | |
64 | ||
65 | while (shortfall-- > 0) { | |
66 | strcat(oline, " "); | |
67 | } | |
68 | oc[1] = EOS; | |
69 | strcat(oline, " | "); | |
70 | for (i = 0; i < len; i++) { | |
71 | int b = dat[i]; | |
72 | ||
73 | /* Map non-printing characters to "." according to the | |
74 | definitions for ISO 8859/1 Latin-1. */ | |
75 | ||
76 | if ((b < ' ') || (b > '~' && b < 160)) { | |
77 | b = '.'; | |
78 | } | |
79 | ||
80 | /* Many existing systems which support Latin-1 lack | |
81 | a definition for character 160, the non-breaking | |
82 | space. Translate this to a space to avoid | |
83 | confusion. */ | |
84 | ||
85 | if (b == 160) { | |
86 | b = ' '; | |
87 | } | |
88 | oc[0] = (char) b; | |
89 | strcat(oline, oc); | |
90 | } | |
91 | } | |
92 | strcat(oline, "\n"); | |
93 | fputs(oline, out); | |
94 | } | |
95 | ||
96 | /* INTERPLINE -- Interpret a line of input. */ | |
97 | ||
98 | static int interpline(line, lineno, out) | |
99 | char *line; | |
100 | int lineno; | |
101 | FILE *out; | |
102 | { | |
103 | char *cp = line; | |
104 | char c; | |
105 | unsigned long lfaddr; | |
106 | int gfaddr = FALSE; | |
107 | ||
108 | /* Scan the line for a possible alternative format information | |
109 | following a vertical bar and delete it. */ | |
110 | ||
111 | while ((c = *cp++) != EOS) { | |
112 | if (c == '|') { | |
113 | cp[-1] = EOS; | |
114 | break; | |
115 | } | |
116 | } | |
117 | ||
118 | /* Scan the line for a file address terminated by a colon. Save | |
119 | the file address. */ | |
120 | ||
121 | cp = line; | |
122 | ||
123 | while ((c = *cp++) != EOS) { | |
124 | if (c == ':') { | |
125 | int sa; | |
126 | char tchar; | |
127 | ||
128 | cp[-1] = EOS; | |
129 | sa = sscanf(line, scanaddr, &lfaddr, &tchar); | |
130 | if (sa == 0 || (sa > 1 && tchar != EOS)) { | |
131 | fprintf(stderr, | |
132 | "Bad file address \"%s\" on line %d:\n", | |
133 | line, lineno); | |
134 | return FALSE; | |
135 | } | |
136 | gfaddr = TRUE; | |
137 | cp[-1] = ':'; | |
138 | break; | |
139 | } | |
140 | } | |
141 | if (!gfaddr) { | |
142 | cp = line; | |
143 | } | |
144 | if (!streaming) { | |
145 | if (!gfaddr) { | |
146 | fprintf(stderr, "File address missing on line %d:\n", lineno); | |
147 | fprintf(stderr, "%s\n", line); | |
148 | return FALSE; | |
149 | } | |
150 | if (lfaddr != fileaddr) { | |
151 | fprintf(stderr, "File address sequence error on line %d.\n", | |
152 | lineno); | |
153 | fprintf(stderr, " Expected "); | |
154 | fprintf(stderr, addrformat, fileaddr); | |
155 | fprintf(stderr, ", received "); | |
156 | fprintf(stderr, addrformat, lfaddr); | |
157 | fprintf(stderr, ".\n"); | |
158 | fprintf(stderr, "%s\n", line); | |
159 | return FALSE; | |
160 | } | |
161 | } | |
162 | ||
163 | while ((c = *cp++) != EOS) { | |
164 | if (!isspace(c)) { | |
165 | int scanl, nscan, dvalue; | |
166 | char termchar; | |
167 | ||
168 | if (((scanl = sscanf(cp - 1, scandata, &dvalue, &nscan, &termchar)) == 0) || | |
169 | (dvalue < 0) || (dvalue > 255) || | |
170 | (scanl == 2 && !isspace(termchar))) { | |
171 | fprintf(stderr, "Improper value, \"%s\" on line %d:\n", | |
172 | cp - 1, lineno); | |
173 | fprintf(stderr, "%s\n", line); | |
174 | fprintf(stderr, "Bytes must be specified as digits separated by white space.\n"); | |
175 | return FALSE; | |
176 | } | |
177 | putc((char) dvalue, out); | |
178 | fileaddr++; | |
179 | cp += nscan; | |
180 | } | |
181 | } | |
182 | return TRUE; | |
183 | } | |
184 | ||
185 | /* Main program */ | |
186 | ||
187 | int main(argc, argv) | |
188 | int argc; char *argv[]; | |
189 | { | |
190 | int i, b, bp, cdata = FALSE, f = 0; | |
191 | char *cp, *clabel, opt; | |
192 | FILE *in = stdin, *out = stdout; | |
193 | ||
194 | for (i = 1; i < argc; i++) { | |
195 | cp = argv[i]; | |
196 | if (*cp == '-') { | |
197 | opt = *(++cp); | |
198 | if (islower(opt)) { | |
199 | opt = (char) toupper(opt); | |
200 | } | |
201 | switch (opt) { | |
202 | ||
203 | case 'A': /* -Af -- Set address format */ | |
204 | opt = cp[1]; | |
205 | if (islower(opt)) { | |
206 | opt = (char) toupper(opt); | |
207 | } | |
208 | switch (opt) { | |
209 | case 'D': | |
210 | strcpy(addrformat, "%8d"); | |
211 | strcpy(scanaddr, "%ld%c"); | |
212 | break; | |
213 | ||
214 | case 'H': | |
215 | case 'X': | |
216 | strcpy(addrformat, "%6X"); | |
217 | strcpy(scanaddr, "%lx%c"); | |
218 | break; | |
219 | ||
220 | case 'O': | |
221 | strcpy(addrformat, "%8o"); | |
222 | strcpy(scanaddr, "%lo%c"); | |
223 | break; | |
224 | ||
225 | default: | |
226 | fprintf(stderr, | |
227 | "Invalid address format '%c'. Must be D, H, or O.\n", cp[1]); | |
228 | return 2; | |
229 | } | |
230 | break; | |
231 | ||
232 | case 'C': | |
233 | doublechar = TRUE; | |
234 | break; | |
235 | ||
236 | case 'D': | |
237 | cdata = TRUE; | |
238 | clabel = cp + 1; | |
239 | break; | |
240 | ||
241 | case 'L': | |
242 | loading = TRUE; | |
243 | break; | |
244 | ||
245 | case 'N': /* -Nf -- Set numeric dump format */ | |
246 | opt = cp[1]; | |
247 | if (islower(opt)) { | |
248 | opt = (char) toupper(opt); | |
249 | } | |
250 | switch (opt) { | |
251 | case 'D': | |
252 | strcpy(dataformat1, "%3d"); | |
253 | strcpy(scandata, "%d%n%c"); | |
254 | break; | |
255 | ||
256 | case 'H': | |
257 | case 'X': | |
258 | strcpy(dataformat1, "%02X"); | |
259 | strcpy(scandata, "%x%n%c"); | |
260 | break; | |
261 | ||
262 | case 'O': | |
263 | strcpy(dataformat1, "%03o"); | |
264 | strcpy(scandata, "%o%n%c"); | |
265 | break; | |
266 | ||
267 | default: | |
268 | fprintf(stderr, | |
269 | "Invalid numeric dump format '%c'. Must be D, H, or O.\n", cp[1]); | |
270 | return 2; | |
271 | } | |
272 | break; | |
273 | ||
274 | case 'S': | |
275 | streaming = TRUE; | |
276 | break; | |
277 | ||
278 | case '?': | |
279 | case 'H': | |
280 | case 'U': | |
281 | fprintf(stderr, "XD -- Extended dump. Call with xd [input [output]]\n"); | |
282 | fprintf(stderr, "\n"); | |
283 | fprintf(stderr, " Options:\n"); | |
284 | fprintf(stderr, " -af Print addresses in f = Decimal, Hex, or Octal\n"); | |
285 | fprintf(stderr, " -c Dump as ISO characters\n"); | |
286 | fprintf(stderr, " -dlabel Dump as a C data declaration\n"); | |
287 | fprintf(stderr, " -l Load file from hex dump\n"); | |
288 | fprintf(stderr, " -nf Numeric dump in f = Decimal, Hex, or Octal\n"); | |
289 | fprintf(stderr, " -s Stream load (don't check file addresses)\n"); | |
290 | fprintf(stderr, " -u Print this message\n"); | |
291 | fprintf(stderr, "\nBy John Walker, http://www.fourmilab.ch/\n"); | |
292 | fprintf(stderr,"Version %s\n", Version); | |
293 | return 0; | |
294 | } | |
295 | } else { | |
296 | switch (f) { | |
297 | case 0: | |
298 | ||
299 | /** Warning! On systems which distinguish text mode and | |
300 | binary I/O (MS-DOS, Macintosh, etc.) the modes in these | |
301 | open statements will have to be made conditional based | |
302 | upon whether an encode or decode is being done, which | |
303 | will have to be specified earlier. But it's worse: if | |
304 | input or output is from standard input or output, the | |
305 | mode will have to be changed on the fly, which is | |
306 | generally system and compiler dependent. 'Twasn't me | |
307 | who couldn't conform to Unix CR/LF convention, so | |
308 | don't ask me to write the code to work around | |
309 | Apple and Microsoft's incompatible standards. | |
310 | ||
311 | This file contains code, conditional on _WIN32, which | |
312 | sets binary mode using the method prescribed by | |
313 | Microsoft Visual C 1.52 ("Monkey C"); this may | |
314 | require modification if you're using a different | |
315 | compiler or release of Monkey C. */ | |
316 | ||
317 | if ((in = fopen(cp, loading ? "r" : "rb")) == NULL) { | |
318 | fprintf(stderr, "Cannot open input file %s\n", cp); | |
319 | return 2; | |
320 | } | |
321 | f++; | |
322 | break; | |
323 | ||
324 | case 1: | |
325 | if ((out = fopen(cp, loading ? "wb" : "w")) == NULL) { | |
326 | fprintf(stderr, "Cannot open output file %s\n", cp); | |
327 | return 2; | |
328 | } | |
329 | f++; | |
330 | break; | |
331 | ||
332 | default: | |
333 | fprintf(stderr, "Too many file names specified.\n"); | |
334 | } | |
335 | } | |
336 | } | |
337 | ||
338 | #ifdef _WIN32 | |
339 | ||
340 | /* If input is from standard input and we aren't loading | |
341 | from a dump file, set the input file mode to binary. */ | |
342 | ||
343 | if ((in == stdin) && (!loading)) { | |
344 | _setmode(_fileno(in), _O_BINARY); | |
345 | } | |
346 | ||
347 | /* If output is to standard output and we're loading a | |
348 | binary file from a dump, set the output file mode to | |
349 | binary. */ | |
350 | ||
351 | if ((out == stdout) && (loading)) { | |
352 | _setmode(_fileno(out), _O_BINARY); | |
353 | } | |
354 | #endif | |
355 | ||
356 | bp = 0; | |
357 | fileaddr = 0; | |
358 | ||
359 | if (loading) { | |
360 | char in_line[256]; | |
361 | int lineno = 0; | |
362 | ||
363 | while (fgets(in_line, (sizeof in_line) - 2, in)) { | |
364 | lineno++; | |
365 | if (!interpline(in_line, lineno, out)) { | |
366 | fclose(out); | |
367 | return 2; | |
368 | } | |
369 | } | |
370 | } else { | |
371 | if (cdata) { | |
372 | char cout[80]; | |
373 | ||
374 | fprintf(out, "unsigned char %s[] = {\n", | |
375 | clabel[0] == EOS ? "xd_data" : clabel); | |
376 | strcpy(cout, " "); | |
377 | ||
378 | while ((b = getc(in)) != EOF) { | |
379 | if (strlen(cout) > 72) { | |
380 | fprintf(out, "%s\n", cout); | |
381 | strcpy(cout, " "); | |
382 | } | |
383 | sprintf(cout + strlen(cout), "%d,", b); | |
384 | } | |
385 | if (strlen(cout) > 4) { | |
386 | cout[strlen(cout) - 1] = EOS; /* Strip trailing comma */ | |
387 | fprintf(out, "%s\n", cout); | |
388 | } | |
389 | fprintf(out, "};\n"); | |
390 | } else { | |
391 | while ((b = getc(in)) != EOF) { | |
392 | if (bp >= bytesperline) { | |
393 | outline(out, lineecho, bp); | |
394 | bp = 0; | |
395 | fileaddr += bytesperline; | |
396 | } | |
397 | lineecho[bp++] = (char) b; | |
398 | } | |
399 | ||
400 | if (bp > 0) { | |
401 | outline(out, lineecho, bp); | |
402 | } | |
403 | } | |
404 | } | |
405 | return 0; | |
406 | } |
11 | 11 | #include "node.hpp" |
12 | 12 | #include "shaders.hpp" |
13 | 13 | #include "ui.hpp" |
14 | #include "viewport.hpp" | |
14 | 15 | |
15 | 16 | void error_callback(int error, const char* description) { |
16 | 17 | std::cerr << "[E] glfw error: " << description << std::endl; |
51 | 52 | nodes.nodes.push_back(ana::node{glm::vec2(0, 0), glm::vec2(0.1, 0.2), "hello"}); |
52 | 53 | nodes.nodes.push_back(ana::node{glm::vec2(-0.5, -0.5), glm::vec2(0.2, 0.2), "hello"}); |
53 | 54 | |
54 | ana::ui ui; | |
55 | ana::viewport viewport; | |
55 | 56 | |
56 | 57 | ImGui::CreateContext(); |
57 | //ImGuiIO &io = ImGui::GetIO(); | |
58 | ana::ui::init_imgui(); | |
59 | ||
58 | 60 | ImGui_ImplGlfw_InitForOpenGL(window, true); |
59 | 61 | ImGui_ImplOpenGL3_Init("#version 410"); |
60 | 62 | |
79 | 81 | case SDL_WINDOWEVENT: |
80 | 82 | if (ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { |
81 | 83 | glViewport(0, 0, ev.window.data1, ev.window.data2); |
82 | ui.set_aspect(ev.window.data1, ev.window.data2); | |
84 | viewport.set_aspect(ev.window.data1, ev.window.data2); | |
83 | 85 | } |
84 | 86 | break; |
85 | 87 | |
86 | 88 | case SDL_MOUSEWHEEL: |
87 | 89 | if (!io.WantCaptureMouse && ev.wheel.y != 0) { |
88 | ui.view_step(ev.wheel.y); | |
90 | viewport.view_step(ev.wheel.y); | |
89 | 91 | } |
90 | 92 | break; |
91 | 93 | |
92 | 94 | case SDL_MOUSEMOTION: |
93 | 95 | if (!io.WantCaptureMouse && ev.motion.state & SDL_BUTTON_MMASK) { |
94 | ui.translate(-ev.motion.xrel, ev.motion.yrel); | |
96 | viewport.translate(-ev.motion.xrel, ev.motion.yrel); | |
95 | 97 | } |
96 | 98 | break; |
97 | 99 | } |
108 | 110 | glClearColor(0.15f, 0.12f, 0.1f, 1.0f); |
109 | 111 | glClear(GL_COLOR_BUFFER_BIT); |
110 | 112 | |
111 | ana::shaders::draw(ui, nodes); | |
113 | ana::shaders::draw(viewport, nodes); | |
112 | 114 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); |
113 | 115 | |
114 | 116 | glfwMakeContextCurrent(window); |
161 | 161 | return true; |
162 | 162 | } |
163 | 163 | |
164 | void draw(const ana::ui &ui, const ana::nodeset &nodes) { | |
164 | void draw(const ana::viewport &viewport, const ana::nodeset &nodes) { | |
165 | 165 | static GLfloat *node_data = nullptr; |
166 | 166 | static size_t node_data_size = 0; |
167 | 167 | |
185 | 185 | glBufferData(GL_ARRAY_BUFFER, elems * sizeof(GLfloat), node_data, GL_DYNAMIC_DRAW); INSERT_GL_ERROR_CHECK; |
186 | 186 | |
187 | 187 | glm::mat4 m = glm::ortho( |
188 | ui.center.x - ui.view, ui.center.x + ui.view, | |
189 | ui.center.y - ui.view * ui.aspect, ui.center.y + ui.view * ui.aspect, | |
188 | viewport.center.x - viewport.view, viewport.center.x + viewport.view, | |
189 | viewport.center.y - viewport.view * viewport.aspect, viewport.center.y + viewport.view * viewport.aspect, | |
190 | 190 | -1.f, 1.f); |
191 | 191 | glUniformMatrix4fv(node_viewport_attr, 1, GL_FALSE, glm::value_ptr(m)); |
192 | 192 |
1 | 1 | |
2 | 2 | #include <GL/glew.h> |
3 | 3 | #include "node.hpp" |
4 | #include "ui.hpp" | |
4 | #include "viewport.hpp" | |
5 | 5 | |
6 | 6 | namespace ana { |
7 | 7 | namespace shaders { |
9 | 9 | bool init(void); |
10 | 10 | void destroy(void); |
11 | 11 | void set_aspect(int w, int h); |
12 | void draw(const ana::ui &ui, const ana::nodeset &nodes); | |
12 | void draw(const ana::viewport &ui, const ana::nodeset &nodes); | |
13 | 13 | } |
14 | 14 | } |
0 | #include "font.hpp" | |
0 | 1 | #include "ui.hpp" |
2 | #include "imgui.h" | |
1 | 3 | |
2 | 4 | namespace ana { |
5 | namespace ui { | |
3 | 6 | |
4 | void ui::set_aspect(int w, int h) { | |
5 | width = w; | |
6 | height = h; | |
7 | aspect = (float) h / w; | |
8 | } | |
9 | ||
10 | void ui::view_step(int dir) { | |
11 | if (dir > 0) { | |
12 | view *= 1.2f; | |
13 | if (view > 20) { | |
14 | view = 20; | |
15 | } | |
16 | } else if (dir < 0) { | |
17 | view /= 1.2f; | |
18 | if (view < 0.05f) { | |
19 | view = 0.05f; | |
20 | } | |
21 | } | |
22 | } | |
23 | ||
24 | void ui::translate(int xpixels, int ypixels) { | |
25 | float x = 2.f * view * xpixels / width; | |
26 | float y = 2.f * view * aspect * ypixels / height; | |
27 | center.x += x; | |
28 | center.y += y; | |
7 | void init_imgui() { | |
8 | ImGuiIO &io = ImGui::GetIO(); | |
9 | io.Fonts->AddFontFromMemoryTTF(font_data, font_data_len, 16); | |
29 | 10 | } |
30 | 11 | |
31 | 12 | } |
13 | } |
0 | 0 | #pragma once |
1 | 1 | |
2 | #include <glm/glm.hpp> | |
3 | ||
4 | 2 | namespace ana { |
5 | struct ui { | |
6 | float aspect = 1.f; | |
7 | float view = 1.f; | |
8 | int width = 1; | |
9 | int height = 1; | |
10 | glm::vec2 center{0.f, 0.f}; | |
11 | ||
12 | void set_aspect(int w, int h); | |
13 | void view_step(int dir); | |
14 | void translate(int xpixels, int ypixels); | |
15 | }; | |
3 | namespace ui { | |
4 | void init_imgui(); | |
16 | 5 | } |
6 | } |
0 | #include "viewport.hpp" | |
1 | ||
2 | namespace ana { | |
3 | ||
4 | void viewport::set_aspect(int w, int h) { | |
5 | width = w; | |
6 | height = h; | |
7 | aspect = (float) h / w; | |
8 | } | |
9 | ||
10 | void viewport::view_step(int dir) { | |
11 | if (dir > 0) { | |
12 | view *= 1.2f; | |
13 | if (view > 20) { | |
14 | view = 20; | |
15 | } | |
16 | } else if (dir < 0) { | |
17 | view /= 1.2f; | |
18 | if (view < 0.05f) { | |
19 | view = 0.05f; | |
20 | } | |
21 | } | |
22 | } | |
23 | ||
24 | void viewport::translate(int xpixels, int ypixels) { | |
25 | float x = 2.f * view * xpixels / width; | |
26 | float y = 2.f * view * aspect * ypixels / height; | |
27 | center.x += x; | |
28 | center.y += y; | |
29 | } | |
30 | ||
31 | } |
0 | #pragma once | |
1 | ||
2 | #include <glm/glm.hpp> | |
3 | ||
4 | namespace ana { | |
5 | struct viewport { | |
6 | float aspect = 1.f; | |
7 | float view = 1.f; | |
8 | int width = 1; | |
9 | int height = 1; | |
10 | glm::vec2 center{0.f, 0.f}; | |
11 | ||
12 | void set_aspect(int w, int h); | |
13 | void view_step(int dir); | |
14 | void translate(int xpixels, int ypixels); | |
15 | }; | |
16 | } |
0 | /* | |
1 | ||
2 | Extended dump and load utility | |
3 | ||
4 | by John Walker | |
5 | http://www.fourmilab.ch/ | |
6 | ||
7 | This program is in the public domain. | |
8 | ||
9 | */ | |
10 | ||
11 | #define Version \ | |
12 | "1.4 -- October 2017" | |
13 | ||
14 | ||
15 | #include <stdio.h> | |
16 | #include <ctype.h> | |
17 | #include <string.h> | |
18 | #ifdef _WIN32 | |
19 | #include <fcntl.h> | |
20 | #include <io.h> | |
21 | #endif | |
22 | ||
23 | #define FALSE 0 | |
24 | #define TRUE 1 | |
25 | ||
26 | #define EOS '\0' | |
27 | ||
28 | static char addrformat[80] = "%6X"; | |
29 | static char scanaddr[80] = "%lx%c"; | |
30 | ||
31 | static char dataformat1[80] = "%02X"; | |
32 | static char scandata[80] = "%x%n%c"; | |
33 | ||
34 | static int bytesperline = 16, doublechar = FALSE, | |
35 | dflen = 2, loading = FALSE, streaming = FALSE; | |
36 | static unsigned long fileaddr; | |
37 | static unsigned char lineecho[32]; | |
38 | ||
39 | /* OUTLINE -- Edit a line of binary data into the selected output | |
40 | format. */ | |
41 | ||
42 | static void outline(out, dat, len) | |
43 | FILE *out; | |
44 | unsigned char *dat; | |
45 | int len; | |
46 | { | |
47 | char oline[132]; | |
48 | int i; | |
49 | ||
50 | sprintf(oline, addrformat, fileaddr); | |
51 | strcat(oline, ":"); | |
52 | for (i = 0; i < len; i++) { | |
53 | char outedit[80]; | |
54 | ||
55 | sprintf(outedit, dataformat1, dat[i]); | |
56 | strcat(oline, (i == (bytesperline / 2)) ? " " : " "); | |
57 | strcat(oline, outedit); | |
58 | } | |
59 | ||
60 | if (doublechar) { | |
61 | char oc[2]; | |
62 | int shortfall = ((bytesperline - len) * (dflen + 1)) + | |
63 | (len <= (bytesperline / 2) ? 1 : 0); | |
64 | ||
65 | while (shortfall-- > 0) { | |
66 | strcat(oline, " "); | |
67 | } | |
68 | oc[1] = EOS; | |
69 | strcat(oline, " | "); | |
70 | for (i = 0; i < len; i++) { | |
71 | int b = dat[i]; | |
72 | ||
73 | /* Map non-printing characters to "." according to the | |
74 | definitions for ISO 8859/1 Latin-1. */ | |
75 | ||
76 | if ((b < ' ') || (b > '~' && b < 160)) { | |
77 | b = '.'; | |
78 | } | |
79 | ||
80 | /* Many existing systems which support Latin-1 lack | |
81 | a definition for character 160, the non-breaking | |
82 | space. Translate this to a space to avoid | |
83 | confusion. */ | |
84 | ||
85 | if (b == 160) { | |
86 | b = ' '; | |
87 | } | |
88 | oc[0] = (char) b; | |
89 | strcat(oline, oc); | |
90 | } | |
91 | } | |
92 | strcat(oline, "\n"); | |
93 | fputs(oline, out); | |
94 | } | |
95 | ||
96 | /* INTERPLINE -- Interpret a line of input. */ | |
97 | ||
98 | static int interpline(line, lineno, out) | |
99 | char *line; | |
100 | int lineno; | |
101 | FILE *out; | |
102 | { | |
103 | char *cp = line; | |
104 | char c; | |
105 | unsigned long lfaddr; | |
106 | int gfaddr = FALSE; | |
107 | ||
108 | /* Scan the line for a possible alternative format information | |
109 | following a vertical bar and delete it. */ | |
110 | ||
111 | while ((c = *cp++) != EOS) { | |
112 | if (c == '|') { | |
113 | cp[-1] = EOS; | |
114 | break; | |
115 | } | |
116 | } | |
117 | ||
118 | /* Scan the line for a file address terminated by a colon. Save | |
119 | the file address. */ | |
120 | ||
121 | cp = line; | |
122 | ||
123 | while ((c = *cp++) != EOS) { | |
124 | if (c == ':') { | |
125 | int sa; | |
126 | char tchar; | |
127 | ||
128 | cp[-1] = EOS; | |
129 | sa = sscanf(line, scanaddr, &lfaddr, &tchar); | |
130 | if (sa == 0 || (sa > 1 && tchar != EOS)) { | |
131 | fprintf(stderr, | |
132 | "Bad file address \"%s\" on line %d:\n", | |
133 | line, lineno); | |
134 | return FALSE; | |
135 | } | |
136 | gfaddr = TRUE; | |
137 | cp[-1] = ':'; | |
138 | break; | |
139 | } | |
140 | } | |
141 | if (!gfaddr) { | |
142 | cp = line; | |
143 | } | |
144 | if (!streaming) { | |
145 | if (!gfaddr) { | |
146 | fprintf(stderr, "File address missing on line %d:\n", lineno); | |
147 | fprintf(stderr, "%s\n", line); | |
148 | return FALSE; | |
149 | } | |
150 | if (lfaddr != fileaddr) { | |
151 | fprintf(stderr, "File address sequence error on line %d.\n", | |
152 | lineno); | |
153 | fprintf(stderr, " Expected "); | |
154 | fprintf(stderr, addrformat, fileaddr); | |
155 | fprintf(stderr, ", received "); | |
156 | fprintf(stderr, addrformat, lfaddr); | |
157 | fprintf(stderr, ".\n"); | |
158 | fprintf(stderr, "%s\n", line); | |
159 | return FALSE; | |
160 | } | |
161 | } | |
162 | ||
163 | while ((c = *cp++) != EOS) { | |
164 | if (!isspace(c)) { | |
165 | int scanl, nscan, dvalue; | |
166 | char termchar; | |
167 | ||
168 | if (((scanl = sscanf(cp - 1, scandata, &dvalue, &nscan, &termchar)) == 0) || | |
169 | (dvalue < 0) || (dvalue > 255) || | |
170 | (scanl == 2 && !isspace(termchar))) { | |
171 | fprintf(stderr, "Improper value, \"%s\" on line %d:\n", | |
172 | cp - 1, lineno); | |
173 | fprintf(stderr, "%s\n", line); | |
174 | fprintf(stderr, "Bytes must be specified as digits separated by white space.\n"); | |
175 | return FALSE; | |
176 | } | |
177 | putc((char) dvalue, out); | |
178 | fileaddr++; | |
179 | cp += nscan; | |
180 | } | |
181 | } | |
182 | return TRUE; | |
183 | } | |
184 | ||
185 | /* Main program */ | |
186 | ||
187 | int main(argc, argv) | |
188 | int argc; char *argv[]; | |
189 | { | |
190 | int i, b, bp, cdata = FALSE, f = 0; | |
191 | char *cp, *clabel, opt; | |
192 | FILE *in = stdin, *out = stdout; | |
193 | ||
194 | for (i = 1; i < argc; i++) { | |
195 | cp = argv[i]; | |
196 | if (*cp == '-') { | |
197 | opt = *(++cp); | |
198 | if (islower(opt)) { | |
199 | opt = (char) toupper(opt); | |
200 | } | |
201 | switch (opt) { | |
202 | ||
203 | case 'A': /* -Af -- Set address format */ | |
204 | opt = cp[1]; | |
205 | if (islower(opt)) { | |
206 | opt = (char) toupper(opt); | |
207 | } | |
208 | switch (opt) { | |
209 | case 'D': | |
210 | strcpy(addrformat, "%8d"); | |
211 | strcpy(scanaddr, "%ld%c"); | |
212 | break; | |
213 | ||
214 | case 'H': | |
215 | case 'X': | |
216 | strcpy(addrformat, "%6X"); | |
217 | strcpy(scanaddr, "%lx%c"); | |
218 | break; | |
219 | ||
220 | case 'O': | |
221 | strcpy(addrformat, "%8o"); | |
222 | strcpy(scanaddr, "%lo%c"); | |
223 | break; | |
224 | ||
225 | default: | |
226 | fprintf(stderr, | |
227 | "Invalid address format '%c'. Must be D, H, or O.\n", cp[1]); | |
228 | return 2; | |
229 | } | |
230 | break; | |
231 | ||
232 | case 'C': | |
233 | doublechar = TRUE; | |
234 | break; | |
235 | ||
236 | case 'D': | |
237 | cdata = TRUE; | |
238 | clabel = cp + 1; | |
239 | break; | |
240 | ||
241 | case 'L': | |
242 | loading = TRUE; | |
243 | break; | |
244 | ||
245 | case 'N': /* -Nf -- Set numeric dump format */ | |
246 | opt = cp[1]; | |
247 | if (islower(opt)) { | |
248 | opt = (char) toupper(opt); | |
249 | } | |
250 | switch (opt) { | |
251 | case 'D': | |
252 | strcpy(dataformat1, "%3d"); | |
253 | strcpy(scandata, "%d%n%c"); | |
254 | break; | |
255 | ||
256 | case 'H': | |
257 | case 'X': | |
258 | strcpy(dataformat1, "%02X"); | |
259 | strcpy(scandata, "%x%n%c"); | |
260 | break; | |
261 | ||
262 | case 'O': | |
263 | strcpy(dataformat1, "%03o"); | |
264 | strcpy(scandata, "%o%n%c"); | |
265 | break; | |
266 | ||
267 | default: | |
268 | fprintf(stderr, | |
269 | "Invalid numeric dump format '%c'. Must be D, H, or O.\n", cp[1]); | |
270 | return 2; | |
271 | } | |
272 | break; | |
273 | ||
274 | case 'S': | |
275 | streaming = TRUE; | |
276 | break; | |
277 | ||
278 | case '?': | |
279 | case 'H': | |
280 | case 'U': | |
281 | fprintf(stderr, "XD -- Extended dump. Call with xd [input [output]]\n"); | |
282 | fprintf(stderr, "\n"); | |
283 | fprintf(stderr, " Options:\n"); | |
284 | fprintf(stderr, " -af Print addresses in f = Decimal, Hex, or Octal\n"); | |
285 | fprintf(stderr, " -c Dump as ISO characters\n"); | |
286 | fprintf(stderr, " -dlabel Dump as a C data declaration\n"); | |
287 | fprintf(stderr, " -l Load file from hex dump\n"); | |
288 | fprintf(stderr, " -nf Numeric dump in f = Decimal, Hex, or Octal\n"); | |
289 | fprintf(stderr, " -s Stream load (don't check file addresses)\n"); | |
290 | fprintf(stderr, " -u Print this message\n"); | |
291 | fprintf(stderr, "\nBy John Walker, http://www.fourmilab.ch/\n"); | |
292 | fprintf(stderr,"Version %s\n", Version); | |
293 | return 0; | |
294 | } | |
295 | } else { | |
296 | switch (f) { | |
297 | case 0: | |
298 | ||
299 | /** Warning! On systems which distinguish text mode and | |
300 | binary I/O (MS-DOS, Macintosh, etc.) the modes in these | |
301 | open statements will have to be made conditional based | |
302 | upon whether an encode or decode is being done, which | |
303 | will have to be specified earlier. But it's worse: if | |
304 | input or output is from standard input or output, the | |
305 | mode will have to be changed on the fly, which is | |
306 | generally system and compiler dependent. 'Twasn't me | |
307 | who couldn't conform to Unix CR/LF convention, so | |
308 | don't ask me to write the code to work around | |
309 | Apple and Microsoft's incompatible standards. | |
310 | ||
311 | This file contains code, conditional on _WIN32, which | |
312 | sets binary mode using the method prescribed by | |
313 | Microsoft Visual C 1.52 ("Monkey C"); this may | |
314 | require modification if you're using a different | |
315 | compiler or release of Monkey C. */ | |
316 | ||
317 | if ((in = fopen(cp, loading ? "r" : "rb")) == NULL) { | |
318 | fprintf(stderr, "Cannot open input file %s\n", cp); | |
319 | return 2; | |
320 | } | |
321 | f++; | |
322 | break; | |
323 | ||
324 | case 1: | |
325 | if ((out = fopen(cp, loading ? "wb" : "w")) == NULL) { | |
326 | fprintf(stderr, "Cannot open output file %s\n", cp); | |
327 | return 2; | |
328 | } | |
329 | f++; | |
330 | break; | |
331 | ||
332 | default: | |
333 | fprintf(stderr, "Too many file names specified.\n"); | |
334 | } | |
335 | } | |
336 | } | |
337 | ||
338 | #ifdef _WIN32 | |
339 | ||
340 | /* If input is from standard input and we aren't loading | |
341 | from a dump file, set the input file mode to binary. */ | |
342 | ||
343 | if ((in == stdin) && (!loading)) { | |
344 | _setmode(_fileno(in), _O_BINARY); | |
345 | } | |
346 | ||
347 | /* If output is to standard output and we're loading a | |
348 | binary file from a dump, set the output file mode to | |
349 | binary. */ | |
350 | ||
351 | if ((out == stdout) && (loading)) { | |
352 | _setmode(_fileno(out), _O_BINARY); | |
353 | } | |
354 | #endif | |
355 | ||
356 | bp = 0; | |
357 | fileaddr = 0; | |
358 | ||
359 | if (loading) { | |
360 | char in_line[256]; | |
361 | int lineno = 0; | |
362 | ||
363 | while (fgets(in_line, (sizeof in_line) - 2, in)) { | |
364 | lineno++; | |
365 | if (!interpline(in_line, lineno, out)) { | |
366 | fclose(out); | |
367 | return 2; | |
368 | } | |
369 | } | |
370 | } else { | |
371 | if (cdata) { | |
372 | char cout[80]; | |
373 | unsigned long len = 0; | |
374 | ||
375 | fprintf(out, "#include <stddef.h>\n"); | |
376 | fprintf(out, "unsigned char %s[] = {\n", | |
377 | clabel[0] == EOS ? "xd_data" : clabel); | |
378 | strcpy(cout, " "); | |
379 | ||
380 | while ((b = getc(in)) != EOF) { | |
381 | len++; | |
382 | if (strlen(cout) > 72) { | |
383 | fprintf(out, "%s\n", cout); | |
384 | strcpy(cout, " "); | |
385 | } | |
386 | sprintf(cout + strlen(cout), "%d,", b); | |
387 | } | |
388 | if (strlen(cout) > 4) { | |
389 | cout[strlen(cout) - 1] = EOS; /* Strip trailing comma */ | |
390 | fprintf(out, "%s\n", cout); | |
391 | } | |
392 | fprintf(out, "};\n"); | |
393 | fprintf(out, "size_t %s_len = %ul;\n", clabel[0] == EOS ? "xd_data" : clabel, len); | |
394 | } else { | |
395 | while ((b = getc(in)) != EOF) { | |
396 | if (bp >= bytesperline) { | |
397 | outline(out, lineecho, bp); | |
398 | bp = 0; | |
399 | fileaddr += bytesperline; | |
400 | } | |
401 | lineecho[bp++] = (char) b; | |
402 | } | |
403 | ||
404 | if (bp > 0) { | |
405 | outline(out, lineecho, bp); | |
406 | } | |
407 | } | |
408 | } | |
409 | return 0; | |
410 | } |