|
|
1.1 ! root 1: ! 2: #import <AppKit/AppKit.h> ! 3: #include "../ref_soft/r_local.h" ! 4: ! 5: /* ! 6: ==================================================================== ! 7: ! 8: OPENSTEP specific stuff ! 9: ! 10: ==================================================================== ! 11: */ ! 12: ! 13: @interface QuakeView : NSView ! 14: @end ! 15: ! 16: NSWindow *vid_window_i; ! 17: QuakeView *vid_view_i; ! 18: ! 19: unsigned *buffernative; ! 20: ! 21: //=========================================================== ! 22: ! 23: ! 24: int Draw_SetResolution (void); ! 25: ! 26: #define TYPE_FULLSCREEN 0 ! 27: #define TYPE_WINDOWED 1 ! 28: #define TYPE_STRETCHED 2 ! 29: ! 30: #define NUM_RESOLUTIONS 7 ! 31: int resolutions[NUM_RESOLUTIONS][2] = { ! 32: {320,200}, {320,240}, {400,300}, {512,384}, {640,480}, {800,600}, {1024,768} }; ! 33: ! 34: qboolean available[NUM_RESOLUTIONS][3]; ! 35: int mode_res = 0, mode_type = TYPE_WINDOWED; ! 36: ! 37: byte gammatable[256]; // palette is sent through this ! 38: unsigned current_palette[256]; ! 39: unsigned gamma_palette[256]; ! 40: ! 41: int cursor_res, cursor_type; ! 42: ! 43: cvar_t *vid_x; ! 44: cvar_t *vid_y; ! 45: cvar_t *vid_mode; ! 46: cvar_t *vid_stretched; ! 47: cvar_t *vid_fullscreen; ! 48: cvar_t *draw_gamma; ! 49: ! 50: void Draw_BuildGammaTable (void); ! 51: ! 52: /* ! 53: ==================================================================== ! 54: ! 55: MENU INTERACTION ! 56: ! 57: ==================================================================== ! 58: */ ! 59: ! 60: void FindModes (void) ! 61: { ! 62: if (mode_res < 0 || mode_res >= NUM_RESOLUTIONS) ! 63: mode_res = 0; ! 64: if (mode_type < 0 || mode_type > 3) ! 65: mode_type = 1; ! 66: ! 67: } ! 68: ! 69: void RM_Print (int x, int y, char *s) ! 70: { ! 71: while (*s) ! 72: { ! 73: Draw_Char (x, y, (*s)+128); ! 74: s++; ! 75: x += 8; ! 76: } ! 77: } ! 78: ! 79: /* ! 80: ================ ! 81: Draw_MenuDraw ! 82: ================ ! 83: */ ! 84: void Draw_MenuDraw (void) ! 85: { ! 86: int i, j; ! 87: int y; ! 88: char string[32]; ! 89: ! 90: Draw_Pic ( 4, 4, "vidmodes"); ! 91: ! 92: RM_Print (80, 32, "fullscreen windowed stretched"); ! 93: RM_Print (80, 40, "---------- -------- ---------"); ! 94: y = 50; ! 95: ! 96: // draw background behind selected mode ! 97: Draw_Fill ( (mode_type+1)*80, y+(mode_res)*10, 40,10, 8); ! 98: ! 99: // draw available grid ! 100: for (i=0 ; i<NUM_RESOLUTIONS ; i++, y+= 10) ! 101: { ! 102: sprintf (string, "%ix%i", resolutions[i][0], resolutions[i][1]); ! 103: RM_Print (0, y, string); ! 104: for (j=0 ; j<3 ; j++) ! 105: if (available[i][j]) ! 106: RM_Print ( 80 + j*80, y, "*"); ! 107: } ! 108: ! 109: // draw the cursor ! 110: Draw_Char (80 + cursor_type*80, 50 + cursor_res*10, 128 + 12+((int)(r_newrefdef.time*4)&1)); ! 111: } ! 112: ! 113: ! 114: #define K_TAB 9 ! 115: #define K_ENTER 13 ! 116: #define K_ESCAPE 27 ! 117: #define K_SPACE 32 ! 118: ! 119: // normal keys should be passed as lowercased ascii ! 120: ! 121: #define K_BACKSPACE 127 ! 122: #define K_UPARROW 128 ! 123: #define K_DOWNARROW 129 ! 124: #define K_LEFTARROW 130 ! 125: #define K_RIGHTARROW 131 ! 126: ! 127: /* ! 128: ================ ! 129: Draw_MenuKey ! 130: ================ ! 131: */ ! 132: void Draw_MenuKey (int key) ! 133: { ! 134: switch (key) ! 135: { ! 136: case K_LEFTARROW: ! 137: cursor_type--; ! 138: if (cursor_type < 0) ! 139: cursor_type = 2; ! 140: break; ! 141: ! 142: case K_RIGHTARROW: ! 143: cursor_type++; ! 144: if (cursor_type > 2) ! 145: cursor_type = 0; ! 146: break; ! 147: ! 148: case K_UPARROW: ! 149: cursor_res--; ! 150: if (cursor_res < 0) ! 151: cursor_res = NUM_RESOLUTIONS-1; ! 152: break; ! 153: ! 154: case K_DOWNARROW: ! 155: cursor_res++; ! 156: if (cursor_res >= NUM_RESOLUTIONS) ! 157: cursor_res = 0; ! 158: break; ! 159: ! 160: case K_ENTER: ! 161: ri.Cmd_ExecuteText (EXEC_NOW, va("vid_mode %i", cursor_res)); ! 162: switch (cursor_type) ! 163: { ! 164: case TYPE_FULLSCREEN: ! 165: ri.Cmd_ExecuteText (EXEC_NOW, "vid_fullscreen 1"); ! 166: ri.Cmd_ExecuteText (EXEC_NOW, "vid_stretched 0"); ! 167: break; ! 168: case TYPE_WINDOWED: ! 169: ri.Cmd_ExecuteText (EXEC_NOW, "vid_fullscreen 0"); ! 170: ri.Cmd_ExecuteText (EXEC_NOW, "vid_stretched 0"); ! 171: break; ! 172: case TYPE_STRETCHED: ! 173: ri.Cmd_ExecuteText (EXEC_NOW, "vid_fullscreen 0"); ! 174: ri.Cmd_ExecuteText (EXEC_NOW, "vid_stretched 1"); ! 175: break; ! 176: } ! 177: ! 178: mode_res = cursor_res; ! 179: mode_type = cursor_type; ! 180: Draw_SetResolution (); ! 181: break; ! 182: ! 183: default: ! 184: break; ! 185: } ! 186: } ! 187: ! 188: //=========================================================== ! 189: ! 190: ! 191: /* ! 192: ================ ! 193: Draw_SetResolution ! 194: ! 195: The vid structure will be filled in on return ! 196: Also allocates the z buffer and surface cache ! 197: ================ ! 198: */ ! 199: int Draw_SetResolution (void) ! 200: { ! 201: NSRect content; ! 202: ! 203: if (vid_mode->value < 0) ! 204: ri.Cmd_ExecuteText (EXEC_NOW, "vid_mode 0"); ! 205: if (vid_mode->value >= NUM_RESOLUTIONS) ! 206: ri.Cmd_ExecuteText (EXEC_NOW, va("vid_mode %i", NUM_RESOLUTIONS-1)); ! 207: ! 208: vid_mode->modified = false; ! 209: vid_fullscreen->modified = false; ! 210: vid_stretched->modified = false; ! 211: ! 212: // free nativebuffer ! 213: if (buffernative) ! 214: { ! 215: free (buffernative); ! 216: buffernative = NULL; ! 217: } ! 218: ! 219: // free z buffer ! 220: if (d_pzbuffer) ! 221: { ! 222: free (d_pzbuffer); ! 223: d_pzbuffer = NULL; ! 224: } ! 225: // free surface cache ! 226: if (sc_base) ! 227: { ! 228: D_FlushCaches (); ! 229: free (sc_base); ! 230: sc_base = NULL; ! 231: } ! 232: ! 233: vid.width = resolutions[(int)(vid_mode->value)][0]; ! 234: vid.height = resolutions[(int)(vid_mode->value)][1]; ! 235: ! 236: vid.win_width = vid.width; ! 237: vid.win_height = vid.height; ! 238: if (vid_stretched->value) ! 239: { ! 240: vid.win_width <<= 1; ! 241: vid.win_height <<= 1; ! 242: } ! 243: ! 244: vid.aspect = 1; ! 245: vid.buffer = malloc (vid.width*vid.height); ! 246: vid.rowbytes = vid.width; ! 247: d_pzbuffer = malloc(vid.width*vid.height*2); ! 248: buffernative = malloc(vid.width*vid.height*4); ! 249: ! 250: D_InitCaches (); ! 251: ! 252: Sys_SetPalette ((byte *)d_8to24table); ! 253: ! 254: if (vid_view_i) ! 255: [vid_view_i unlockFocus]; ! 256: if (vid_window_i) ! 257: [vid_window_i close]; ! 258: // ! 259: // open a window ! 260: // ! 261: content = NSMakeRect (vid_x->value,vid_y->value,vid.win_width, vid.win_height); ! 262: vid_window_i = [[NSWindow alloc] ! 263: initWithContentRect: content ! 264: styleMask: NSTitledWindowMask ! 265: backing: NSBackingStoreRetained ! 266: defer: NO ! 267: ]; ! 268: ! 269: [vid_window_i setDelegate: vid_window_i]; ! 270: [vid_window_i display]; ! 271: [NSApp activateIgnoringOtherApps: YES]; ! 272: [vid_window_i makeKeyAndOrderFront: nil]; ! 273: ! 274: // NSPing (); ! 275: ! 276: content.origin.x = content.origin.y = 0; ! 277: vid_view_i = [[QuakeView alloc] initWithFrame: content]; ! 278: [vid_window_i setContentView: vid_view_i]; ! 279: [vid_window_i makeFirstResponder: vid_view_i]; ! 280: [vid_window_i setDelegate: vid_view_i]; ! 281: ! 282: // [vid_window_i addToEventMask: NS_FLAGSCHANGEDMASK]; ! 283: [vid_window_i setTitle: @"Bitmap Quake Console"]; ! 284: [vid_window_i makeKeyAndOrderFront: nil]; ! 285: ! 286: // leave focus locked forever ! 287: [vid_view_i lockFocus]; ! 288: ! 289: ri.VID_SetSize (vid.width, vid.height); ! 290: ! 291: return 0; ! 292: } ! 293: ! 294: /* ! 295: @@@@@@@@@@@@@@@@@@@@@ ! 296: Draw_Init ! 297: ! 298: @@@@@@@@@@@@@@@@@@@@@ ! 299: */ ! 300: int Draw_Init (void *window) ! 301: { ! 302: [NSApplication sharedApplication]; ! 303: [NSApp finishLaunching]; ! 304: ! 305: ri.Con_Printf (PRINT_ALL, "refresh version: "REF_VERSION"\n"); ! 306: ! 307: vid_x = ri.Cvar_Get ("vid_x", "0", CVAR_ARCHIVE); ! 308: vid_y = ri.Cvar_Get ("vid_y", "0", CVAR_ARCHIVE); ! 309: vid_mode = ri.Cvar_Get ("vid_mode", "0", CVAR_ARCHIVE); ! 310: vid_fullscreen = ri.Cvar_Get ("vid_fullscreen", "0", CVAR_ARCHIVE); ! 311: vid_stretched = ri.Cvar_Get ("vid_stretched", "0", CVAR_ARCHIVE); ! 312: draw_gamma = ri.Cvar_Get ("gamma", "1", CVAR_ARCHIVE); ! 313: ! 314: Draw_GetPalette (); ! 315: ! 316: Draw_BuildGammaTable (); ! 317: ! 318: // get the lighting colormap ! 319: ri.FS_LoadFile ("gfx/colormap.lmp", (void **)&vid.colormap); ! 320: if (!vid.colormap) ! 321: { ! 322: ri.Con_Printf (PRINT_ALL, "ERROR: Couldn't load gfx/colormap.lmp"); ! 323: return -1; ! 324: } ! 325: ! 326: Draw_SetResolution (); ! 327: ! 328: R_Init (); ! 329: ! 330: return 0; ! 331: } ! 332: ! 333: ! 334: /* ! 335: @@@@@@@@@@@@@@@@@@@@@ ! 336: Draw_Shutdown ! 337: ! 338: @@@@@@@@@@@@@@@@@@@@@ ! 339: */ ! 340: void Draw_Shutdown (void) ! 341: { ! 342: R_Shutdown (); ! 343: } ! 344: ! 345: ! 346: /* ! 347: @@@@@@@@@@@@@@@@@@@@@ ! 348: Draw_BuildGammaTable ! 349: ! 350: @@@@@@@@@@@@@@@@@@@@@ ! 351: */ ! 352: void Draw_BuildGammaTable (void) ! 353: { ! 354: int i, inf; ! 355: float g; ! 356: ! 357: draw_gamma->modified = false; ! 358: g = draw_gamma->value; ! 359: ! 360: if (g == 1.0) ! 361: { ! 362: for (i=0 ; i<256 ; i++) ! 363: gammatable[i] = i; ! 364: return; ! 365: } ! 366: ! 367: for (i=0 ; i<256 ; i++) ! 368: { ! 369: inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5; ! 370: if (inf < 0) ! 371: inf = 0; ! 372: if (inf > 255) ! 373: inf = 255; ! 374: gammatable[i] = inf; ! 375: } ! 376: } ! 377: ! 378: ! 379: /* ! 380: @@@@@@@@@@@@@@@@@@@@@ ! 381: Draw_BeginFram ! 382: ! 383: @@@@@@@@@@@@@@@@@@@@@ ! 384: */ ! 385: void Draw_BeginFrame (void) ! 386: { ! 387: if (vid_mode->modified || vid_fullscreen->modified ! 388: || vid_stretched->modified) ! 389: Draw_SetResolution (); ! 390: ! 391: if (draw_gamma->modified) ! 392: { ! 393: Draw_BuildGammaTable (); ! 394: Sys_SetPalette ((byte *)current_palette); ! 395: } ! 396: ! 397: // MGL_beginDirectAccess(); ! 398: // vid.buffer = mgldc->surface; ! 399: // vid.rowbytes = mgldc->mi.bytesPerLine; ! 400: } ! 401: ! 402: ! 403: /* ! 404: @@@@@@@@@@@@@@@@@@@@@ ! 405: Draw_EndFrame ! 406: ! 407: @@@@@@@@@@@@@@@@@@@@@ ! 408: */ ! 409: void Draw_EndFrame (void) ! 410: { ! 411: int i, c; ! 412: int bps, spp, bpp, bpr; ! 413: unsigned char *planes[5]; ! 414: NSRect bounds; ! 415: ! 416: // translate to 24 bit color ! 417: c = vid.width*vid.height; ! 418: for (i=0 ; i<c ; i++) ! 419: buffernative[i] = gamma_palette[vid.buffer[i]]; ! 420: ! 421: bps = 8; ! 422: spp = 3; ! 423: bpp = 32; ! 424: bpr = vid.width * 4; ! 425: planes[0] = (unsigned char *)buffernative; ! 426: ! 427: bounds = [vid_view_i bounds]; ! 428: ! 429: NSDrawBitmap( ! 430: bounds, ! 431: vid.width, ! 432: vid.height, ! 433: bps, ! 434: spp, ! 435: bpp, ! 436: bpr, ! 437: NO, ! 438: NO, ! 439: @"NSDeviceRGBColorSpace", ! 440: planes ! 441: ); ! 442: } ! 443: ! 444: ! 445: //=============================================================================== ! 446: ! 447: #define HUNK_MAGIC 0xffaffaff ! 448: typedef struct ! 449: { ! 450: int magic; ! 451: int length; ! 452: int pad[6]; ! 453: } hunkheader_t; ! 454: ! 455: hunkheader_t *membase; ! 456: int maxsize; ! 457: int cursize; ! 458: ! 459: void *Hunk_Begin (void) ! 460: { ! 461: kern_return_t r; ! 462: ! 463: // reserve a huge chunk of memory, but don't commit any yet ! 464: maxsize = 16*1024*1024; ! 465: cursize = 0; ! 466: membase = NULL; ! 467: r = vm_allocate(task_self(), (vm_address_t *)&membase, maxsize, 1); ! 468: if (!membase || r != KERN_SUCCESS) ! 469: ri.Sys_Error (ERR_FATAL,"vm_allocate failed"); ! 470: membase->magic = HUNK_MAGIC; ! 471: membase->length = maxsize; ! 472: cursize = 32; ! 473: return (void *)((byte *)membase + cursize); ! 474: } ! 475: ! 476: void *Hunk_Alloc (int size) ! 477: { ! 478: // round to cacheline ! 479: size = (size+31)&~31; ! 480: ! 481: cursize += size; ! 482: ! 483: if (cursize > maxsize) ! 484: ri.Sys_Error (ERR_DROP, "Hunk_Alloc overflow"); ! 485: ! 486: memset ((byte *)membase+cursize-size,0,size); ! 487: ! 488: return (void *)((byte *)membase+cursize-size); ! 489: } ! 490: ! 491: int Hunk_End (void) ! 492: { ! 493: kern_return_t r; ! 494: ! 495: // round to pagesize ! 496: cursize = (cursize+vm_page_size)&~(vm_page_size-1); ! 497: membase->length = cursize; ! 498: r = vm_deallocate(task_self(), ! 499: (vm_address_t)((byte *)membase + cursize), ! 500: maxsize - cursize); ! 501: if ( r != KERN_SUCCESS ) ! 502: ri.Sys_Error (ERR_DROP, "vm_deallocate failed"); ! 503: return cursize; ! 504: } ! 505: ! 506: void Hunk_Free (void *base) ! 507: { ! 508: hunkheader_t *h; ! 509: kern_return_t r; ! 510: ! 511: h = ((hunkheader_t *)base) - 1; ! 512: if (h->magic != HUNK_MAGIC) ! 513: ri.Sys_Error (ERR_FATAL, "Hunk_Free: bad magic"); ! 514: ! 515: r = vm_deallocate(task_self(), (vm_address_t)h, h->length); ! 516: if ( r != KERN_SUCCESS ) ! 517: ri.Sys_Error (ERR_DROP, "vm_deallocate failed"); ! 518: } ! 519: ! 520: ! 521: /* ! 522: ================ ! 523: Sys_MakeCodeWriteable ! 524: ================ ! 525: */ ! 526: void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) ! 527: { ! 528: } ! 529: ! 530: ! 531: /* ! 532: ================ ! 533: Sys_SetPalette ! 534: ================ ! 535: */ ! 536: void Sys_SetPalette (byte *palette) ! 537: { ! 538: byte *p; ! 539: int i; ! 540: ! 541: memcpy (current_palette, palette, sizeof(current_palette)); ! 542: p = (byte *)gamma_palette; ! 543: // gamma correct and byte swap ! 544: for (i=0 ; i<256 ; i++, p+=4, palette+=4) ! 545: { ! 546: p[0] = gammatable[palette[0]]; ! 547: p[1] = gammatable[palette[1]]; ! 548: p[2] = gammatable[palette[2]]; ! 549: p[3] = 0xff; ! 550: } ! 551: ! 552: } ! 553: ! 554: ! 555: /* ! 556: ========================================================================== ! 557: ! 558: NEXTSTEP VIEW CLASS ! 559: ! 560: ========================================================================== ! 561: */ ! 562: #include "../client/keys.h" ! 563: ! 564: void IN_ActivateMouse (void); ! 565: void IN_DeactivateMouse (void); ! 566: ! 567: @implementation QuakeView ! 568: ! 569: -(BOOL) acceptsFirstResponder ! 570: { ! 571: return YES; ! 572: } ! 573: ! 574: - (void)windowDidMove: (NSNotification *)note ! 575: { ! 576: NSRect r; ! 577: ! 578: r = [vid_window_i frame]; ! 579: ri.Cmd_ExecuteText (EXEC_NOW, va("vid_x %i", (int)r.origin.x+1)); ! 580: ri.Cmd_ExecuteText (EXEC_NOW, va("vid_y %i", (int)r.origin.y+1)); ! 581: } ! 582: ! 583: - (void)becomeKeyWindow ! 584: { ! 585: IN_ActivateMouse (); ! 586: } ! 587: ! 588: - (void)resignKeyWindow ! 589: { ! 590: IN_DeactivateMouse (); ! 591: } ! 592: ! 593: ! 594: typedef struct ! 595: { ! 596: int source, dest; ! 597: } keymap_t; ! 598: ! 599: keymap_t keymaps[] = ! 600: { ! 601: {103, K_RIGHTARROW}, ! 602: {102, K_LEFTARROW}, ! 603: {100, K_UPARROW}, ! 604: {101, K_DOWNARROW}, ! 605: ! 606: {59, K_F1}, ! 607: {60, K_F2}, ! 608: {61, K_F3}, ! 609: {62, K_F4}, ! 610: {63, K_F5}, ! 611: {64, K_F6}, ! 612: {65, K_F7}, ! 613: {66, K_F8}, ! 614: {67, K_F9}, ! 615: {68, K_F10}, ! 616: {87, K_F11}, ! 617: {88, K_F12}, ! 618: ! 619: {-1,-1} ! 620: }; ! 621: ! 622: keymap_t flagmaps[] = ! 623: { ! 624: {NSShiftKeyMask, K_SHIFT}, ! 625: {NSControlKeyMask, K_CTRL}, ! 626: {NSAlternateKeyMask, K_ALT}, ! 627: {NSCommandKeyMask, K_ALT}, ! 628: ! 629: {-1,-1} ! 630: }; ! 631: ! 632: - (void)mouseDown:(NSEvent *)theEvent ! 633: { ! 634: Key_Event (K_MOUSE1, true); ! 635: } ! 636: - (void)mouseUp:(NSEvent *)theEvent ! 637: { ! 638: Key_Event (K_MOUSE1, false); ! 639: } ! 640: - (void)rightMouseDown:(NSEvent *)theEvent ! 641: { ! 642: Key_Event (K_MOUSE2, true); ! 643: } ! 644: - (void)rightMouseUp:(NSEvent *)theEvent ! 645: { ! 646: Key_Event (K_MOUSE2, false); ! 647: } ! 648: ! 649: ! 650: /* ! 651: =================== ! 652: keyboard methods ! 653: =================== ! 654: */ ! 655: - (void)keyDown:(NSEvent *)theEvent ! 656: { ! 657: int ch; ! 658: keymap_t *km; ! 659: ! 660: // PSobscurecursor (); ! 661: ! 662: // check for non-ascii first ! 663: ch = [theEvent keyCode]; ! 664: for (km=keymaps;km->source!=-1;km++) ! 665: if (ch == km->source) ! 666: { ! 667: Key_Event (km->dest, true); ! 668: return; ! 669: } ! 670: ! 671: ch = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]; ! 672: if (ch >= 'A' && ch <= 'Z') ! 673: ch += 'a' - 'A'; ! 674: if (ch>=256) ! 675: return; ! 676: ! 677: Key_Event (ch, true); ! 678: } ! 679: ! 680: - (void)flagsChanged:(NSEvent *)theEvent ! 681: { ! 682: static int oldflags; ! 683: int newflags; ! 684: int delta; ! 685: keymap_t *km; ! 686: int i; ! 687: ! 688: // PSobscurecursor (); ! 689: newflags = [theEvent modifierFlags]; ! 690: delta = newflags ^ oldflags; ! 691: for (i=0 ; i<32 ; i++) ! 692: { ! 693: if ( !(delta & (1<<i))) ! 694: continue; ! 695: // changed ! 696: for (km=flagmaps;km->source!=-1;km++) ! 697: if ( (1<<i) == km->source) ! 698: { ! 699: if (newflags & (1<<i)) ! 700: Key_Event (km->dest, true); ! 701: else ! 702: Key_Event (km->dest, false); ! 703: } ! 704: ! 705: } ! 706: ! 707: oldflags = newflags; ! 708: } ! 709: ! 710: ! 711: - (void)keyUp:(NSEvent *)theEvent ! 712: { ! 713: int ch; ! 714: keymap_t *km; ! 715: ! 716: // check for non-ascii first ! 717: ch = [theEvent keyCode]; ! 718: for (km=keymaps;km->source!=-1;km++) ! 719: if (ch == km->source) ! 720: { ! 721: Key_Event (km->dest, false); ! 722: return; ! 723: } ! 724: ! 725: ch = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]; ! 726: if (ch >= 'A' && ch <= 'Z') ! 727: ch += 'a' - 'A'; ! 728: if (ch>=256) ! 729: return; ! 730: Key_Event (ch, false); ! 731: } ! 732: ! 733: @end ! 734: ! 735:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.