|
|
1.1 root 1: /*
2: * UAE - The Un*x Amiga Emulator
1.1.1.3 root 3: *
4: * Main program
5: *
6: * Copyright 1995 Ed Hanway
7: * Copyright 1995, 1996, 1997 Bernd Schmidt
1.1 root 8: */
9: #include "sysconfig.h"
10: #include "sysdeps.h"
11: #include <assert.h>
12:
13: #include "config.h"
14: #include "options.h"
1.1.1.15 root 15: #include "threaddep/thread.h"
1.1.1.2 root 16: #include "uae.h"
1.1.1.3 root 17: #include "gensound.h"
1.1.1.13 root 18: #include "audio.h"
1.1.1.3 root 19: #include "sounddep/sound.h"
1.1.1.2 root 20: #include "events.h"
1.1 root 21: #include "memory.h"
22: #include "custom.h"
23: #include "serial.h"
24: #include "newcpu.h"
25: #include "disk.h"
26: #include "debug.h"
27: #include "xwin.h"
1.1.1.20! root 28: #include "inputdevice.h"
1.1 root 29: #include "keybuf.h"
30: #include "gui.h"
31: #include "zfile.h"
32: #include "autoconf.h"
1.1.1.2 root 33: #include "osemu.h"
1.1.1.3 root 34: #include "osdep/exectasks.h"
35: #include "picasso96.h"
1.1.1.6 root 36: #include "bsdsocket.h"
1.1.1.3 root 37: #include "uaeexe.h"
1.1.1.8 root 38: #include "native2amiga.h"
39: #include "scsidev.h"
1.1 root 40:
1.1.1.15 root 41: #ifdef USE_SDL
42: #include "SDL.h"
43: #endif
44:
1.1.1.4 root 45: long int version = 256*65536L*UAEMAJOR + 65536L*UAEMINOR + UAESUBREV;
1.1.1.3 root 46:
1.1.1.6 root 47: struct uae_prefs currprefs, changed_prefs;
1.1.1.4 root 48:
1.1.1.6 root 49: int no_gui = 0;
1.1.1.4 root 50: int joystickpresent = 0;
51: int cloanto_rom = 0;
52:
1.1.1.11 root 53: struct gui_info gui_data;
54:
1.1.1.4 root 55: char warning_buffer[256];
56:
57: char optionsfile[256];
58:
59: /* If you want to pipe printer output to a file, put something like
60: * "cat >>printerfile.tmp" above.
61: * The printer support was only tested with the driver "PostScript" on
62: * Amiga side, using apsfilter for linux to print ps-data.
63: *
64: * Under DOS it ought to be -p LPT1: or -p PRN: but you'll need a
65: * PostScript printer or ghostscript -=SR=-
66: */
67:
68: /* Slightly stupid place for this... */
69: /* ncurses.c might use quite a few of those. */
70: char *colormodes[] = { "256 colors", "32768 colors", "65536 colors",
71: "256 colors dithered", "16 colors dithered", "16 million colors",
72: "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
73: "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
74: "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
75: "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
76: "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
77: };
78:
1.1.1.6 root 79: void discard_prefs (struct uae_prefs *p)
1.1.1.3 root 80: {
1.1.1.6 root 81: struct strlist **ps = &p->unknown_lines;
82: while (*ps) {
83: struct strlist *s = *ps;
84: *ps = s->next;
85: free (s->str);
86: free (s);
87: }
88: free_mountinfo (p->mountinfo);
89: }
90:
91: void default_prefs (struct uae_prefs *p)
92: {
93: strcpy (p->description, "UAE default configuration");
94:
95: p->start_gui = 1;
96: p->start_debugger = 0;
1.1.1.20! root 97: p->leds_on_screen = 1;
1.1.1.6 root 98:
99: p->unknown_lines = 0;
1.1.1.4 root 100: /* Note to porters: please don't change any of these options! UAE is supposed
101: * to behave identically on all platforms if possible. */
1.1.1.6 root 102: p->illegal_mem = 0;
103: p->no_xhair = 0;
104: p->use_serial = 0;
105: p->serial_demand = 0;
106: p->parallel_demand = 0;
107:
1.1.1.20! root 108: p->jport0 = JSEM_MICE;
! 109: p->jport1 = JSEM_JOYS;
! 110:
1.1.1.6 root 111: p->keyboard_lang = KBD_LANG_US;
112: p->emul_accuracy = 2;
113: p->test_drawing_speed = 0;
114:
115: p->produce_sound = 0;
1.1.1.18 root 116: p->sound_stereo = 0;
1.1.1.6 root 117: p->sound_bits = DEFAULT_SOUND_BITS;
118: p->sound_freq = DEFAULT_SOUND_FREQ;
119: p->sound_minbsiz = DEFAULT_SOUND_MINB;
120: p->sound_maxbsiz = DEFAULT_SOUND_MAXB;
1.1.1.9 root 121: p->sound_interpol = 0;
1.1.1.6 root 122:
1.1.1.8 root 123: p->gfx_framerate = 1;
1.1.1.6 root 124: p->gfx_width = 800;
125: p->gfx_height = 600;
126: p->gfx_lores = 0;
1.1.1.15 root 127: p->gfx_linedbl = 2;
1.1.1.6 root 128: p->gfx_afullscreen = 0;
129: p->gfx_pfullscreen = 0;
130: p->gfx_correct_aspect = 0;
131: p->gfx_xcenter = 0;
132: p->gfx_ycenter = 0;
133: p->color_mode = 0;
134:
135: p->x11_use_low_bandwidth = 0;
136: p->x11_use_mitshm = 0;
137: p->x11_hide_cursor = 1;
138:
1.1.1.8 root 139: p->svga_no_linear = 0;
140:
1.1.1.10 root 141: p->curses_reverse_video = 0;
142:
1.1.1.8 root 143: p->win32_middle_mouse = 0;
144: p->win32_logfile = 0;
145: p->win32_iconified_nospeed = 0;
146: p->win32_iconified_nosound = 0;
1.1.1.16 root 147: p->win32_no_overlay = 0;
1.1.1.8 root 148:
1.1.1.6 root 149: p->immediate_blits = 0;
1.1.1.14 root 150: p->collision_level = 1;
1.1.1.6 root 151:
152: strcpy (p->df[0], "df0.adf");
153: strcpy (p->df[1], "df1.adf");
154: strcpy (p->df[2], "df2.adf");
155: strcpy (p->df[3], "df3.adf");
156:
157: strcpy (p->romfile, "kick.rom");
158: strcpy (p->keyfile, "");
159: strcpy (p->prtname, DEFPRTNAME);
160:
161: strcpy (p->path_rom, "./");
162: strcpy (p->path_floppy, "./");
163: strcpy (p->path_hardfile, "./");
164:
1.1.1.15 root 165: strcpy (p->prtname, "");
166: strcpy (p->sername, "");
167:
1.1.1.11 root 168: p->m68k_speed = 0;
1.1.1.6 root 169: p->cpu_level = 2;
170: p->cpu_compatible = 0;
171: p->address_space_24 = 0;
172:
173: p->fastmem_size = 0x00000000;
174: p->a3000mem_size = 0x00000000;
175: p->z3fastmem_size = 0x00000000;
176: p->chipmem_size = 0x00200000;
177: p->bogomem_size = 0x00000000;
178: p->gfxmem_size = 0x00000000;
1.1 root 179:
1.1.1.11 root 180: p->nr_floppies = 4;
181:
1.1.1.6 root 182: p->mountinfo = alloc_mountinfo ();
1.1.1.20! root 183: inputdevice_default_prefs (p);
1.1.1.4 root 184: }
1.1 root 185:
1.1.1.4 root 186: void fixup_prefs_dimensions (struct uae_prefs *prefs)
187: {
188: if (prefs->gfx_width < 320)
189: prefs->gfx_width = 320;
190: if (prefs->gfx_height < 200)
191: prefs->gfx_height = 200;
192: if (prefs->gfx_height > 300 && ! prefs->gfx_linedbl)
193: prefs->gfx_height = 300;
194: if (prefs->gfx_height > 600)
195: prefs->gfx_height = 600;
1.1 root 196:
1.1.1.4 root 197: prefs->gfx_width += 7; /* X86.S wants multiples of 4 bytes, might be 8 in the future. */
198: prefs->gfx_width &= ~7;
199: }
1.1.1.3 root 200:
1.1.1.4 root 201: static void fix_options (void)
1.1.1.2 root 202: {
1.1.1.3 root 203: int err = 0;
204:
1.1.1.4 root 205: if ((currprefs.chipmem_size & (currprefs.chipmem_size - 1)) != 0
206: || currprefs.chipmem_size < 0x80000
207: || currprefs.chipmem_size > 0x800000)
1.1.1.3 root 208: {
1.1.1.4 root 209: currprefs.chipmem_size = 0x200000;
1.1.1.16 root 210: write_log ("Unsupported chipmem size!\n");
1.1.1.3 root 211: err = 1;
212: }
1.1.1.4 root 213: if ((currprefs.fastmem_size & (currprefs.fastmem_size - 1)) != 0
214: || (currprefs.fastmem_size != 0 && (currprefs.fastmem_size < 0x100000 || currprefs.fastmem_size > 0x800000)))
1.1.1.3 root 215: {
1.1.1.4 root 216: currprefs.fastmem_size = 0;
1.1.1.16 root 217: write_log ("Unsupported fastmem size!\n");
1.1.1.3 root 218: err = 1;
219: }
1.1.1.4 root 220: if ((currprefs.gfxmem_size & (currprefs.gfxmem_size - 1)) != 0
1.1.1.16 root 221: || (currprefs.gfxmem_size != 0 && (currprefs.gfxmem_size < 0x100000 || currprefs.gfxmem_size > 0x2000000)))
1.1.1.3 root 222: {
1.1.1.16 root 223: write_log ("Unsupported graphics card memory size %lx!\n", currprefs.gfxmem_size);
1.1.1.4 root 224: currprefs.gfxmem_size = 0;
1.1.1.3 root 225: err = 1;
226: }
1.1.1.4 root 227: if ((currprefs.z3fastmem_size & (currprefs.z3fastmem_size - 1)) != 0
228: || (currprefs.z3fastmem_size != 0 && (currprefs.z3fastmem_size < 0x100000 || currprefs.z3fastmem_size > 0x4000000)))
1.1.1.3 root 229: {
1.1.1.4 root 230: currprefs.z3fastmem_size = 0;
1.1.1.16 root 231: write_log ("Unsupported Zorro III fastmem size!\n");
1.1.1.3 root 232: err = 1;
233: }
1.1.1.4 root 234: if (currprefs.address_space_24 && (currprefs.gfxmem_size != 0 || currprefs.z3fastmem_size != 0)) {
235: currprefs.z3fastmem_size = currprefs.gfxmem_size = 0;
1.1.1.16 root 236: write_log ("Can't use a graphics card or Zorro III fastmem when using a 24 bit\n"
1.1.1.3 root 237: "address space - sorry.\n");
238: }
1.1.1.4 root 239: if ((currprefs.bogomem_size & (currprefs.bogomem_size - 1)) != 0
240: || (currprefs.bogomem_size != 0 && (currprefs.bogomem_size < 0x80000 || currprefs.bogomem_size > 0x100000)))
1.1.1.3 root 241: {
1.1.1.4 root 242: currprefs.bogomem_size = 0;
1.1.1.16 root 243: write_log ("Unsupported bogomem size!\n");
1.1.1.3 root 244: err = 1;
245: }
246:
1.1.1.4 root 247: if (currprefs.chipmem_size > 0x200000 && currprefs.fastmem_size != 0) {
1.1.1.16 root 248: write_log ("You can't use fastmem and more than 2MB chip at the same time!\n");
1.1.1.4 root 249: currprefs.fastmem_size = 0;
1.1.1.3 root 250: err = 1;
251: }
1.1.1.13 root 252: #if 0
1.1.1.4 root 253: if (currprefs.m68k_speed < -1 || currprefs.m68k_speed > 20) {
1.1.1.16 root 254: write_log ("Bad value for -w parameter: must be -1, 0, or within 1..20.\n");
1.1.1.3 root 255: currprefs.m68k_speed = 4;
256: err = 1;
257: }
1.1.1.13 root 258: #endif
1.1.1.20! root 259:
1.1.1.3 root 260: if (currprefs.produce_sound < 0 || currprefs.produce_sound > 3) {
1.1.1.16 root 261: write_log ("Bad value for -S parameter: enable value must be within 0..3\n");
1.1.1.3 root 262: currprefs.produce_sound = 0;
263: err = 1;
264: }
1.1.1.4 root 265: if (currprefs.cpu_level < 2 && currprefs.z3fastmem_size > 0) {
1.1.1.16 root 266: write_log ("Z3 fast memory can't be used with a 68000/68010 emulation. It\n"
1.1.1.3 root 267: "requires a 68020 emulation. Turning off Z3 fast memory.\n");
1.1.1.4 root 268: currprefs.z3fastmem_size = 0;
1.1.1.3 root 269: err = 1;
270: }
1.1.1.8 root 271: if (currprefs.gfxmem_size > 0 && (currprefs.cpu_level < 2 || currprefs.address_space_24)) {
1.1.1.16 root 272: write_log ("Picasso96 can't be used with a 68000/68010 or 68EC020 emulation. It\n"
1.1.1.3 root 273: "requires a 68020 emulation. Turning off Picasso96.\n");
1.1.1.4 root 274: currprefs.gfxmem_size = 0;
1.1.1.3 root 275: err = 1;
276: }
1.1.1.8 root 277: #ifndef BSDSOCKET_SUPPORTED
278: if (currprefs.socket_emu) {
1.1.1.16 root 279: write_log ("Compile-time option of BSDSOCKET_SUPPORTED was not enabled. You can't use bsd-socket emulation.\n");
1.1.1.8 root 280: currprefs.socket_emu = 0;
281: err = 1;
282: }
283: #endif
1.1.1.3 root 284:
1.1.1.11 root 285: if (currprefs.nr_floppies < 1 || currprefs.nr_floppies > 4) {
1.1.1.16 root 286: write_log ("Invalid number of floppies. Using 4.\n");
1.1.1.11 root 287: currprefs.nr_floppies = 4;
288: err = 1;
289: }
1.1.1.14 root 290: if (currprefs.collision_level < 0 || currprefs.collision_level > 3) {
1.1.1.16 root 291: write_log ("Invalid collision support level. Using 1.\n");
1.1.1.14 root 292: currprefs.collision_level = 1;
293: err = 1;
294: }
1.1.1.11 root 295:
1.1.1.3 root 296: if (err)
1.1.1.16 root 297: write_log ("Please use \"uae -h\" to get usage information.\n");
1.1.1.3 root 298: }
299:
300: int quit_program = 0;
301:
1.1.1.20! root 302: void uae_reset (int hardreset)
1.1.1.3 root 303: {
1.1.1.20! root 304: if (quit_program == 0) {
1.1.1.3 root 305: quit_program = -2;
1.1.1.20! root 306: if (hardreset)
! 307: quit_program = -3;
! 308: }
1.1.1.3 root 309: }
310:
311: void uae_quit (void)
312: {
313: if (quit_program != -1)
314: quit_program = -1;
1.1.1.2 root 315: }
1.1 root 316:
1.1.1.6 root 317: #ifndef DONT_PARSE_CMDLINE
1.1.1.3 root 318:
1.1.1.6 root 319: void usage (void)
320: {
1.1.1.3 root 321: }
322:
1.1.1.6 root 323: void parse_cmdline (int argc, char **argv)
324: {
1.1.1.8 root 325: int i;
326: for (i = 1; i < argc; i++) {
327: if (strncmp (argv[i], "-config=", 8) == 0)
328: cfgfile_load (&currprefs, argv[i] + 8);
329: /* Check for new-style "-f xxx" argument, where xxx is config-file */
330: else if (strcmp (argv[i], "-f") == 0) {
331: if (i + 1 == argc)
1.1.1.17 root 332: write_log ("Missing argument for '-f' option.\n");
1.1.1.8 root 333: else
334: cfgfile_load (&currprefs, argv[++i]);
335: } else if (strcmp (argv[i], "-s") == 0) {
336: if (i + 1 == argc)
1.1.1.17 root 337: write_log ("Missing argument for '-s' option.\n");
1.1.1.8 root 338: else
339: cfgfile_parse_line (&currprefs, argv[++i]);
340: } else if (strcmp (argv[i], "-h") == 0 || strcmp (argv[i], "-help") == 0) {
1.1.1.6 root 341: usage ();
342: exit (0);
1.1.1.9 root 343: } else {
344: if (argv[i][0] == '-' && argv[i][1] != '\0') {
345: const char *arg = argv[i] + 2;
1.1.1.10 root 346: int extra_arg = *arg == '\0';
347: if (extra_arg)
1.1.1.9 root 348: arg = i + 1 < argc ? argv[i + 1] : 0;
1.1.1.10 root 349: if (parse_cmdline_option (argv[i][1], arg) && extra_arg)
350: i++;
1.1.1.9 root 351: }
1.1.1.3 root 352: }
1.1 root 353: }
354: }
355: #endif
356:
1.1.1.4 root 357: static void parse_cmdline_and_init_file (int argc, char **argv)
1.1 root 358: {
1.1.1.3 root 359: char *home;
1.1 root 360:
1.1.1.6 root 361: strcpy (optionsfile, "");
1.1 root 362:
1.1.1.3 root 363: #ifdef OPTIONS_IN_HOME
1.1.1.4 root 364: home = getenv ("HOME");
365: if (home != NULL && strlen (home) < 240)
1.1 root 366: {
1.1.1.4 root 367: strcpy (optionsfile, home);
368: strcat (optionsfile, "/");
1.1 root 369: }
370: #endif
371:
1.1.1.4 root 372: strcat (optionsfile, OPTIONSFILENAME);
1.1 root 373:
1.1.1.6 root 374: if (! cfgfile_load (&currprefs, optionsfile)) {
1.1.1.3 root 375: #ifdef OPTIONS_IN_HOME
1.1.1.6 root 376: /* sam: if not found in $HOME then look in current directory */
1.1.1.3 root 377: strcpy (optionsfile, OPTIONSFILENAME);
1.1.1.6 root 378: cfgfile_load (&currprefs, optionsfile);
1.1.1.2 root 379: #endif
1.1.1.17 root 380: }
1.1.1.2 root 381:
1.1.1.6 root 382: parse_cmdline (argc, argv);
1.1 root 383: }
384:
1.1.1.4 root 385: void reset_all_systems (void)
386: {
1.1.1.14 root 387: init_eventtab ();
388:
389: memory_reset ();
1.1.1.6 root 390: bsdlib_reset ();
1.1.1.4 root 391: filesys_reset ();
392: filesys_start_threads ();
1.1.1.8 root 393: scsidev_reset ();
394: scsidev_start_threads ();
1.1.1.4 root 395: }
396:
1.1.1.2 root 397: /* Okay, this stuff looks strange, but it is here to encourage people who
398: * port UAE to re-use as much of this code as possible. Functions that you
399: * should be using are do_start_program() and do_leave_program(), as well
400: * as real_main(). Some OSes don't call main() (which is braindamaged IMHO,
401: * but unfortunately very common), so you need to call real_main() from
402: * whatever entry point you have. You may want to write your own versions
403: * of start_program() and leave_program() if you need to do anything special.
404: * Add #ifdefs around these as appropriate.
405: */
406:
1.1.1.4 root 407: void do_start_program (void)
1.1.1.2 root 408: {
1.1.1.20! root 409: if (quit_program == -1)
! 410: return;
! 411: inputdevice_updateconfig (&currprefs);
! 412:
1.1.1.3 root 413: /* Do a reset on startup. Whether this is elegant is debatable. */
1.1.1.20! root 414: if (quit_program >= 0)
! 415: quit_program = 2;
1.1.1.4 root 416: m68k_go (1);
1.1.1.2 root 417: }
418:
1.1.1.4 root 419: void do_leave_program (void)
1.1.1.2 root 420: {
1.1.1.4 root 421: graphics_leave ();
1.1.1.20! root 422: inputdevice_close ();
1.1.1.4 root 423: close_sound ();
424: dump_counts ();
425: serial_exit ();
426: zfile_exit ();
427: if (! no_gui)
428: gui_exit ();
1.1.1.15 root 429: #ifdef USE_SDL
430: SDL_Quit ();
431: #endif
432: expansion_cleanup ();
433: memory_cleanup ();
1.1.1.2 root 434: }
435:
1.1.1.4 root 436: void start_program (void)
1.1.1.2 root 437: {
1.1.1.4 root 438: do_start_program ();
1.1.1.2 root 439: }
440:
1.1.1.4 root 441: void leave_program (void)
1.1.1.2 root 442: {
1.1.1.4 root 443: do_leave_program ();
1.1.1.2 root 444: }
445:
1.1.1.4 root 446: void real_main (int argc, char **argv)
1.1 root 447: {
448: FILE *hf;
449:
1.1.1.15 root 450: #ifdef USE_SDL
451: SDL_Init (SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE);
452: #endif
453:
1.1.1.6 root 454: default_prefs (&currprefs);
1.1.1.20! root 455:
1.1.1.4 root 456: if (! graphics_setup ()) {
457: exit (1);
1.1 root 458: }
1.1.1.2 root 459:
1.1 root 460: rtarea_init ();
461: hardfile_install ();
1.1.1.8 root 462: scsidev_install ();
1.1 root 463:
1.1.1.4 root 464: parse_cmdline_and_init_file (argc, argv);
1.1.1.2 root 465:
1.1.1.4 root 466: machdep_init ();
1.1.1.3 root 467:
1.1.1.4 root 468: if (! setup_sound ()) {
1.1.1.16 root 469: write_log ("Sound driver unavailable: Sound output disabled\n");
1.1.1.3 root 470: currprefs.produce_sound = 0;
1.1 root 471: }
1.1.1.20! root 472: inputdevice_init ();
1.1 root 473:
1.1.1.15 root 474: changed_prefs = currprefs;
1.1.1.6 root 475: no_gui = ! currprefs.start_gui;
1.1.1.4 root 476: if (! no_gui) {
477: int err = gui_init ();
1.1.1.15 root 478: currprefs = changed_prefs;
1.1.1.2 root 479: if (err == -1) {
1.1.1.16 root 480: write_log ("Failed to initialize the GUI\n");
1.1.1.2 root 481: } else if (err == -2) {
1.1.1.4 root 482: exit (0);
1.1.1.2 root 483: }
1.1 root 484: }
1.1.1.13 root 485: if (sound_available && currprefs.produce_sound > 1 && ! init_audio ()) {
1.1.1.16 root 486: write_log ("Sound driver unavailable: Sound output disabled\n");
1.1.1.3 root 487: currprefs.produce_sound = 0;
488: }
1.1.1.2 root 489:
1.1.1.4 root 490: fix_options ();
1.1.1.3 root 491: changed_prefs = currprefs;
492:
1.1.1.2 root 493: /* Install resident module to get 8MB chipmem, if requested */
1.1.1.4 root 494: rtarea_setup ();
1.1.1.3 root 495:
1.1.1.4 root 496: keybuf_init (); /* Must come after init_joystick */
1.1.1.3 root 497:
1.1 root 498: expansion_init ();
1.1.1.4 root 499: memory_init ();
500:
501: filesys_install ();
502: gfxlib_install ();
503: emulib_install ();
504: uaeexe_install ();
1.1.1.8 root 505: native2amiga_install ();
1.1.1.4 root 506:
507: custom_init (); /* Must come after memory_init */
508: serial_init ();
509: DISK_init ();
510:
511: reset_frame_rate_hack ();
512: init_m68k(); /* must come after reset_frame_rate_hack (); */
1.1 root 513:
1.1.1.4 root 514: gui_update ();
1.1.1.2 root 515:
1.1.1.3 root 516: if (graphics_init ()) {
517: setup_brkhandler ();
1.1.1.6 root 518: if (currprefs.start_debugger && debuggable ())
1.1.1.3 root 519: activate_debugger ();
520:
521: start_program ();
1.1 root 522: }
1.1.1.3 root 523: leave_program ();
1.1.1.2 root 524: }
525:
1.1.1.3 root 526: #ifndef NO_MAIN_IN_MAIN_C
1.1.1.4 root 527: int main (int argc, char **argv)
1.1.1.2 root 528: {
1.1.1.4 root 529: real_main (argc, argv);
1.1 root 530: return 0;
531: }
1.1.1.3 root 532: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.