some conditional op emulation
[ginge.git] / prep / main.c
CommitLineData
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
32static void *fb_mem;
33static int fb_stride;
34static int fb_x, fb_y;
4d045184 35static int init_done;
3adc9ccb 36
37static char *sskip(char *p)
38{
39 while (p && *p && isspace(*p))
40 p++;
41 return p;
42}
43
44static char *cskip(char *p)
45{
46 while (p && *p && !isspace(*p))
47 p++;
48 return p;
49}
50
adb79840 51static void fb_text_exit(void)
52{
53 if (!init_done)
54 return;
55
56 host_video_finish();
57 init_done = 0;
58}
59
4d045184 60static 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 71static 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
101static 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 136static 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
152static 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
192out:
193 fclose(fi);
194 free(phdr);
195 return ret;
196}
197
adb79840 198static 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
218static 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 }
240const 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
250static 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
262int 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
452pass:
453 fputs(p, fout);
454 }
455
456 fclose(fin);
457
458no_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