499bf01c |
1 | /* |
2 | * GINGE - GINGE Is Not Gp2x Emulator |
3 | * (C) notaz, 2010-2011 |
4 | * |
5 | * This work is licensed under the MAME license, see COPYING file for details. |
6 | */ |
3adc9ccb |
7 | #include <stdio.h> |
8 | #include <stdlib.h> |
9 | #include <string.h> |
10 | #include <stdarg.h> |
11 | #include <unistd.h> |
12 | #include <elf.h> |
13 | #include <sys/stat.h> |
14 | #include <ctype.h> |
15 | |
16 | #include "../common/host_fb.h" |
17 | #include "../common/cmn.h" |
18 | |
19 | #define PFX "ging_prep: " |
20 | #define LOADER_STATIC "ginge_sloader" |
21 | #define LOADER_DYNAMIC "ginge_dyn.sh" |
22 | #define LAUNCHER "gp2xmenu" |
23 | |
4d045184 |
24 | #ifdef PND |
25 | #define WRAP_APP "op_runfbapp " |
26 | #else |
27 | #define WRAP_APP "" |
28 | #endif |
29 | |
3adc9ccb |
30 | #include "font.c" |
31 | |
32 | static void *fb_mem; |
33 | static int fb_stride; |
34 | static int fb_x, fb_y; |
4d045184 |
35 | static int init_done; |
3adc9ccb |
36 | |
37 | static char *sskip(char *p) |
38 | { |
39 | while (p && *p && isspace(*p)) |
40 | p++; |
41 | return p; |
42 | } |
43 | |
44 | static char *cskip(char *p) |
45 | { |
46 | while (p && *p && !isspace(*p)) |
47 | p++; |
48 | return p; |
49 | } |
50 | |
adb79840 |
51 | static void fb_text_exit(void) |
52 | { |
53 | if (!init_done) |
54 | return; |
55 | |
56 | host_video_finish(); |
57 | init_done = 0; |
58 | } |
59 | |
4d045184 |
60 | static void fb_text_init(void) |
61 | { |
62 | int ret = host_video_init(&fb_stride, 1); |
63 | if (ret == 0) |
64 | fb_mem = host_video_flip(); |
65 | fb_x = 4; |
66 | fb_y = 4; |
67 | init_done = 1; |
adb79840 |
68 | atexit(fb_text_exit); |
4d045184 |
69 | } |
70 | |
3adc9ccb |
71 | static void fb_syms_out(void *fbi, int x, int y, int dotsz, int stride, const char *text, int count) |
72 | { |
73 | int v = -1, n = 0, *p; |
74 | int i, l; |
75 | char *fb; |
76 | |
77 | fb = (char *)fbi + x * dotsz + y * stride; |
78 | |
79 | for (i = 0; i < count; i++) |
80 | { |
81 | for (l = 0; l < 8; l++) |
82 | { |
83 | #define pix(fdmask,add) \ |
84 | p = (fontdata8x8[((text[i])*8)+l] & fdmask) ? &v : &n; \ |
85 | memcpy(fb + l*stride + add*dotsz, p, dotsz) |
86 | pix(0x80, 0); |
87 | pix(0x40, 1); |
88 | pix(0x20, 2); |
89 | pix(0x10, 3); |
90 | pix(0x08, 4); |
91 | pix(0x04, 5); |
92 | pix(0x02, 6); |
93 | pix(0x01, 7); |
94 | #undef pix |
95 | } |
96 | fb += dotsz * 8; |
97 | } |
98 | } |
99 | |
100 | // FIXME: y overrun |
101 | static void fb_text_out(char *text) |
102 | { |
103 | int dotsz = 2, w = 320; // hardcoded for now |
104 | char *p, *pe; |
105 | int l; |
106 | |
4d045184 |
107 | if (!init_done) |
108 | fb_text_init(); |
109 | |
3adc9ccb |
110 | if (fb_mem == NULL) |
111 | return; |
112 | |
113 | p = text; |
114 | while (*p) { |
115 | for (; *p && isspace(*p); p++) { |
116 | if (*p == '\n' || fb_x + dotsz * 8 > w) { |
117 | fb_x = 4; |
118 | fb_y += 8; |
119 | } |
120 | if (*p >= 0x20) |
121 | fb_x += 8; |
122 | } |
123 | |
124 | pe = cskip(p); |
125 | l = pe - p; |
126 | if (fb_x + 8 * l > w) { |
127 | fb_x = 4; |
128 | fb_y += 8; |
129 | } |
130 | fb_syms_out(fb_mem, fb_x, fb_y, dotsz, fb_stride, p, l); |
131 | fb_x += 8 * l; |
132 | p = pe; |
133 | } |
134 | } |
135 | |
3adc9ccb |
136 | static void fbprintf(int is_err, const char *format, ...) |
137 | { |
138 | va_list ap; |
139 | char buff[512]; |
140 | |
141 | va_start(ap, format); |
142 | vsnprintf(buff, sizeof(buff), format, ap); |
143 | va_end(ap); |
144 | fputs(buff, is_err ? stderr : stdout); |
145 | |
146 | fb_text_out(buff); |
147 | } |
148 | |
adb79840 |
149 | #define msg(fmt, ...) fbprintf(0, fmt, ##__VA_ARGS__) |
150 | #define err(fmt, ...) fbprintf(1, fmt, ##__VA_ARGS__) |
3adc9ccb |
151 | |
152 | static int id_elf(const char *fname) |
153 | { |
154 | Elf32_Ehdr hdr; |
155 | Elf32_Phdr *phdr = NULL; |
156 | FILE *fi; |
157 | int i, ret = 0; |
158 | |
159 | fi = fopen(fname, "rb"); |
160 | if (fi == NULL) { |
161 | err("open %s: ", fname); |
162 | perror(""); |
163 | return -1; |
164 | } |
165 | |
166 | if (fread(&hdr, 1, sizeof(hdr), fi) != sizeof(hdr)) |
167 | goto out; |
168 | |
169 | if (memcmp(hdr.e_ident, ELFMAG "\x01\x01", SELFMAG + 2) != 0) |
170 | goto out; |
171 | |
172 | if (hdr.e_phentsize != sizeof(Elf32_Phdr) || hdr.e_phnum == 0) |
173 | goto out; |
174 | |
175 | phdr = malloc(hdr.e_phnum * hdr.e_phentsize); |
176 | if (phdr == NULL) |
177 | goto out; |
178 | |
179 | if (fread(phdr, hdr.e_phentsize, hdr.e_phnum, fi) != hdr.e_phnum) |
180 | goto out; |
181 | |
182 | ret = 1; |
183 | |
184 | // do what 'file' does - check for PT_INTERP in program headers |
185 | for (i = 0; i < hdr.e_phnum; i++) { |
186 | if (phdr[i].p_type == PT_INTERP) { |
187 | ret = 2; |
188 | break; |
189 | } |
190 | } |
191 | |
192 | out: |
193 | fclose(fi); |
194 | free(phdr); |
195 | return ret; |
196 | } |
197 | |
adb79840 |
198 | static void dump_args(FILE *fout, char * const argv[]) |
3adc9ccb |
199 | { |
200 | const char *p; |
201 | int i; |
202 | |
adb79840 |
203 | for (i = 0; argv[i] != NULL; i++) { |
3adc9ccb |
204 | if (i != 0) |
205 | fputc(' ', fout); |
206 | fputc('"', fout); |
207 | |
208 | for (p = argv[i]; *p != 0; p++) { |
209 | if (*p == '"') |
210 | fputc('\\', fout); |
211 | fputc(*p, fout); |
212 | } |
213 | |
214 | fputc('"', fout); |
215 | } |
216 | } |
217 | |
218 | static char *get_arg(char *d, size_t size, char *p) |
219 | { |
220 | char *pe; |
221 | int len; |
222 | |
223 | p = sskip(p); |
224 | pe = cskip(p); |
225 | len = pe - p; |
226 | |
227 | if (len > size - 1) { |
228 | err(PFX "get_arg: buff to small: %d/%d\n", len, size); |
229 | len = size - 1; |
230 | } |
231 | strncpy(d, p, len); |
232 | d[len] = 0; |
233 | |
234 | return sskip(pe); |
235 | } |
236 | |
237 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) |
238 | |
239 | #define CB_ENTRY(x) { x, sizeof(x) - 1 } |
240 | const struct { |
241 | const char *name; |
242 | int len; |
243 | } conv_blacklist[] = { |
244 | CB_ENTRY("insmod"), |
245 | CB_ENTRY("modprobe"), |
246 | CB_ENTRY("umount"), |
247 | CB_ENTRY("./cpuctrl_tiny"), |
248 | }; |
249 | |
250 | static int cmd_in_blacklist(char *cmd) |
251 | { |
252 | int i; |
253 | |
254 | cmd = sskip(cmd); |
255 | for (i = 0; i < ARRAY_SIZE(conv_blacklist); i++) |
256 | if (strncmp(cmd, conv_blacklist[i].name, conv_blacklist[i].len) == 0) |
257 | return 1; |
258 | |
259 | return 0; |
260 | } |
261 | |
262 | int main(int argc, char *argv[]) |
263 | { |
264 | static const char out_script[] = "/tmp/ginge_conv.sh"; |
265 | char root_path[512], cwd[512]; |
adb79840 |
266 | char **argv_app = NULL; |
3adc9ccb |
267 | int have_cramfs = 0; |
268 | int rerun_gp2xmenu = 1; |
adb79840 |
269 | int quit_if_no_app = 0; |
3adc9ccb |
270 | FILE *fin, *fout; |
adb79840 |
271 | int i, ret; |
272 | |
273 | for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1] == '-'; i++) { |
274 | if (strcmp(argv[i], "--cleanup") == 0) { |
275 | // as loader may crash eny time, restore screen for the menu |
276 | host_video_init(NULL, 1); |
277 | host_video_finish(); |
278 | quit_if_no_app = 1; |
279 | continue; |
280 | } |
281 | if (strcmp(argv[i], "--nomenu") == 0) { |
282 | rerun_gp2xmenu = 0; |
283 | continue; |
284 | } |
285 | if (strcmp(argv[i], "--") == 0) { |
286 | i++; |
287 | break; |
288 | } |
3adc9ccb |
289 | |
adb79840 |
290 | fprintf(stderr, PFX "ignoring unknown option \"%s\"\n", argv[i]); |
3adc9ccb |
291 | } |
292 | |
adb79840 |
293 | if (argc <= i) { |
294 | if (quit_if_no_app) |
295 | return 0; |
296 | err("usage: %s [opts] <script|program> [args]\n", argv[0]); |
297 | err(" --cleanup - restore framebuffer state\n"); |
298 | err(" --nomenu - don't run menu on exit\n"); |
299 | return 1; |
6ca08393 |
300 | } |
adb79840 |
301 | argv_app = &argv[i]; |
6ca08393 |
302 | |
3adc9ccb |
303 | if (getcwd(cwd, sizeof(cwd)) == NULL) { |
304 | err(PFX "failed to get cwd\n"); |
305 | return 1; |
306 | } |
307 | |
308 | ret = make_local_path(root_path, sizeof(root_path), ""); |
309 | if (ret != 0) { |
310 | err(PFX "failed to generate root path\n"); |
311 | return 1; |
312 | } |
313 | |
314 | fout = fopen(out_script, "w"); |
315 | if (fout == NULL) { |
316 | perror("can't open output script"); |
317 | return 1; |
318 | } |
319 | |
320 | fprintf(fout, "#!/bin/sh\n"); |
321 | |
adb79840 |
322 | ret = id_elf(argv_app[0]); |
3adc9ccb |
323 | if (ret == 1 || ret == 2) { |
adb79840 |
324 | if (cmd_in_blacklist(argv_app[0])) { |
325 | fprintf(stderr, "blacklisted: %s\n", argv_app[0]); |
3adc9ccb |
326 | goto no_in_script; |
327 | } |
328 | } |
329 | |
330 | switch (ret) { |
331 | case 0: |
332 | break; |
333 | |
334 | case 1: |
4d045184 |
335 | fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC); |
adb79840 |
336 | dump_args(fout, argv_app); |
3adc9ccb |
337 | fprintf(fout, "\n"); |
338 | goto no_in_script; |
339 | |
340 | case 2: |
4d045184 |
341 | fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); |
adb79840 |
342 | dump_args(fout, argv_app); |
3adc9ccb |
343 | fprintf(fout, "\n"); |
344 | goto no_in_script; |
345 | |
346 | default: |
347 | return 1; |
348 | } |
349 | |
350 | // assume script |
adb79840 |
351 | fin = fopen(argv_app[0], "r"); |
3adc9ccb |
352 | if (fin == NULL) |
353 | return 1; |
354 | |
355 | while (1) { |
356 | char buff[512], fname[512], *p, *p2; |
357 | |
358 | p = fgets(buff, sizeof(buff), fin); |
359 | if (p == NULL) |
360 | break; |
361 | p = sskip(p); |
362 | |
363 | if (p[0] == '#' && p[1] == '!') |
364 | continue; |
365 | |
366 | if (*p == 0) { |
367 | fputs("\n", fout); |
368 | continue; |
369 | } |
370 | |
371 | // things we are sure we want to pass |
372 | if (*p == '#' || strncmp(p, "export ", 7) == 0) |
373 | goto pass; |
374 | |
375 | // hmh.. |
376 | if (strncmp(p, "exec ", 5) == 0) |
377 | p = sskip(p + 5); |
378 | |
379 | // blacklist some stuff |
380 | if (strncmp(p, "/sbin/", 6) == 0) |
381 | p2 = p + 6; |
382 | else if (strncmp(p, "/bin/", 5) == 0) |
383 | p2 = p + 5; |
384 | else |
385 | p2 = p; |
386 | if (strncmp(p2, "mount ", 6) == 0) { |
387 | p2 = sskip(p2 + 6); |
388 | // cramfs stuff? |
389 | if (strstr(p2, "cramfs")) { |
390 | while (*p2 == '-') { |
391 | // skip option |
392 | p2 = sskip(cskip(p2)); |
393 | p2 = sskip(cskip(p2)); |
394 | } |
395 | if (*p2 == 0) { |
396 | err(PFX "cramfs: missing mount file in \"%s\"?\n", p); |
397 | continue; |
398 | } |
399 | p2 = get_arg(fname, sizeof(fname), p2); |
400 | if (*p2 == 0) { |
401 | err(PFX "cramfs: missing mount point in \"%s\"?\n", p); |
402 | continue; |
403 | } |
404 | get_arg(buff, sizeof(buff), p2); |
405 | fprintf(fout, "if [ `ls %s | wc -l` -eq 0 ]; then\n", buff); |
406 | fprintf(fout, " rmdir \"%s\"\n", buff); // cramfsck doesn't want it |
407 | fprintf(fout, " %stools/cramfsck -x \"%s\" \"%s\"\n", root_path, buff, fname); |
408 | fprintf(fout, "fi\n"); |
409 | have_cramfs = 1; |
410 | } |
411 | continue; |
412 | } |
413 | if (cmd_in_blacklist(p2)) |
414 | continue; |
415 | |
416 | // cd? |
417 | if (strncmp(p, "cd ", 3) == 0) { |
418 | get_arg(fname, sizeof(fname), p + 3); |
419 | if (strncmp(fname, "/usr/gp2x", 9) == 0) |
420 | continue; |
421 | ret = chdir(fname); |
422 | if (ret != 0) { |
423 | err("%s: ", fname); |
424 | perror(""); |
425 | } |
426 | } |
427 | |
428 | // trying to run something from cwd? |
429 | if ((p[0] == '.' && p[1] == '/') || *p == '/') { |
430 | get_arg(fname, sizeof(fname), p); |
431 | p2 = strrchr(fname, '/'); |
432 | if (p2 != NULL && strcmp(p2 + 1, "gp2xmenu") == 0) |
433 | continue; |
434 | |
435 | ret = id_elf(fname); |
436 | switch (ret) { |
437 | case 1: |
438 | printf(PFX "prefixing as static: %s", p); |
4d045184 |
439 | fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC); |
3adc9ccb |
440 | break; |
441 | |
442 | case 2: |
443 | printf(PFX "prefixing as dynamic: %s", p); |
4d045184 |
444 | fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); |
3adc9ccb |
445 | break; |
446 | |
447 | default: |
448 | break; |
449 | } |
450 | } |
451 | |
452 | pass: |
453 | fputs(p, fout); |
454 | } |
455 | |
456 | fclose(fin); |
457 | |
458 | no_in_script: |
6ca08393 |
459 | #ifdef WIZ |
460 | fprintf(fout, "sync\n"); |
adb79840 |
461 | // since we don't know if loader manages to do proper cleanup, |
462 | // need to wait for it's threads to die |
463 | fprintf(fout, "sleep 1\n"); |
6ca08393 |
464 | fprintf(fout, "%sginge_prep --cleanup\n", root_path); |
465 | #endif |
3adc9ccb |
466 | if (rerun_gp2xmenu) { |
467 | fprintf(fout, "cd %s\n", root_path); |
468 | fprintf(fout, "exec %s%s\n", root_path, LAUNCHER); |
469 | } |
470 | |
471 | fclose(fout); |
472 | |
4d045184 |
473 | //msg("starting script..\n"); |
474 | if (have_cramfs) { |
475 | msg("\nsome files need to be unpacked, this may tike a few minutes.\n"); |
476 | #ifdef PND |
477 | msg("Please wait at least while SD LED is active.\n"); |
478 | #endif |
479 | } |
3adc9ccb |
480 | system("echo ---; cat /tmp/ginge_conv.sh; echo ---"); |
481 | chmod(out_script, S_IRWXU|S_IRWXG|S_IRWXO); |
482 | chdir(cwd); |
adb79840 |
483 | fb_text_exit(); |
3adc9ccb |
484 | execlp(out_script, out_script, NULL); |
485 | perror("run out_script"); |
486 | |
487 | return 1; |
488 | } |
489 | |
499bf01c |
490 | // vim:shiftwidth=2:expandtab |