|
|
1.1 root 1: /*
2: Hatari - change.c
3:
1.1.1.8 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1 root 6:
7: This code handles run-time configuration changes. We keep all our
8: configuration details in a structure called 'ConfigureParams'. Before
9: doing he changes, a backup copy is done of this structure. When
10: the changes are done, these are compared to see whether emulator
1.1.1.2 root 11: needs to be rebooted
1.1 root 12: */
1.1.1.2 root 13: const char Change_fileid[] = "Hatari change.c : " __DATE__ " " __TIME__;
1.1 root 14:
15: #include <ctype.h>
16: #include "main.h"
17: #include "configuration.h"
18: #include "audio.h"
19: #include "change.h"
20: #include "dialog.h"
21: #include "floppy.h"
1.1.1.9 ! root 22: #include "fdc.h"
1.1 root 23: #include "gemdos.h"
24: #include "hdc.h"
1.1.1.3 root 25: #include "ide.h"
1.1 root 26: #include "ioMem.h"
27: #include "joy.h"
28: #include "keymap.h"
29: #include "m68000.h"
1.1.1.2 root 30: #include "midi.h"
1.1 root 31: #include "options.h"
32: #include "printer.h"
33: #include "reset.h"
34: #include "rs232.h"
35: #include "screen.h"
36: #include "sound.h"
37: #include "statusbar.h"
38: #include "tos.h"
39: #include "vdi.h"
40: #include "video.h"
41: #include "hatari-glue.h"
42: #if ENABLE_DSP_EMU
43: # include "falcon/dsp.h"
44: #endif
45:
1.1.1.5 root 46: #define DEBUG 0
47: #if DEBUG
1.1.1.9 ! root 48: #define Dprintf(a...) printf(a)
1.1.1.5 root 49: #else
1.1.1.9 ! root 50: #define Dprintf(a...)
1.1.1.5 root 51: #endif
1.1 root 52:
53: /*-----------------------------------------------------------------------*/
54: /**
55: * Check if user needs to be warned that changes will take place after reset.
1.1.1.3 root 56: * Return true if wants to reset.
1.1 root 57: */
58: bool Change_DoNeedReset(CNF_PARAMS *current, CNF_PARAMS *changed)
59: {
1.1.1.9 ! root 60: int i;
! 61:
1.1 root 62: /* Did we change monitor type? If so, must reset */
63: if (current->Screen.nMonitorType != changed->Screen.nMonitorType
64: && (changed->System.nMachineType == MACHINE_FALCON
65: || current->Screen.nMonitorType == MONITOR_TYPE_MONO
66: || changed->Screen.nMonitorType == MONITOR_TYPE_MONO))
1.1.1.3 root 67: return true;
1.1 root 68:
69: /* Did change to GEM VDI display? */
70: if (current->Screen.bUseExtVdiResolutions != changed->Screen.bUseExtVdiResolutions)
1.1.1.3 root 71: return true;
1.1 root 72:
73: /* Did change GEM resolution or color depth? */
74: if (changed->Screen.bUseExtVdiResolutions &&
75: (current->Screen.nVdiWidth != changed->Screen.nVdiWidth
76: || current->Screen.nVdiHeight != changed->Screen.nVdiHeight
77: || current->Screen.nVdiColors != changed->Screen.nVdiColors))
1.1.1.3 root 78: return true;
1.1 root 79:
80: /* Did change TOS ROM image? */
81: if (strcmp(changed->Rom.szTosImageFileName, current->Rom.szTosImageFileName))
1.1.1.3 root 82: return true;
1.1 root 83:
1.1.1.3 root 84: /* Did change ACSI hard disk image? */
1.1.1.9 ! root 85: for (i = 0; i < MAX_ACSI_DEVS; i++)
! 86: {
! 87: if (changed->Acsi[i].bUseDevice != current->Acsi[i].bUseDevice
! 88: || (strcmp(changed->Acsi[i].sDeviceFile, current->Acsi[i].sDeviceFile)
! 89: && changed->Acsi[i].bUseDevice))
! 90: return true;
! 91: }
1.1.1.3 root 92:
1.1.1.4 root 93: /* Did change IDE master hard disk image? */
94: if (changed->HardDisk.bUseIdeMasterHardDiskImage != current->HardDisk.bUseIdeMasterHardDiskImage
95: || strcmp(changed->HardDisk.szIdeMasterHardDiskImage, current->HardDisk.szIdeMasterHardDiskImage))
96: return true;
97:
98: /* Did change IDE slave hard disk image? */
99: if (changed->HardDisk.bUseIdeSlaveHardDiskImage != current->HardDisk.bUseIdeSlaveHardDiskImage
100: || strcmp(changed->HardDisk.szIdeSlaveHardDiskImage, current->HardDisk.szIdeSlaveHardDiskImage))
1.1.1.3 root 101: return true;
1.1 root 102:
103: /* Did change GEMDOS drive? */
104: if (changed->HardDisk.bUseHardDiskDirectories != current->HardDisk.bUseHardDiskDirectories
105: || (strcmp(changed->HardDisk.szHardDiskDirectories[0], current->HardDisk.szHardDiskDirectories[0])
106: && changed->HardDisk.bUseHardDiskDirectories))
1.1.1.3 root 107: return true;
1.1 root 108:
109: /* Did change machine type? */
110: if (changed->System.nMachineType != current->System.nMachineType)
1.1.1.3 root 111: return true;
1.1.1.8 root 112: /* did change ST Blitter? */
113: else if (current->System.nMachineType == MACHINE_ST &&
114: current->System.bBlitter != changed->System.bBlitter)
115: return true;
1.1 root 116:
1.1.1.5 root 117: #if ENABLE_DSP_EMU
118: /* enabling DSP needs reset (disabling it not) */
119: if (current->System.nDSPType != DSP_TYPE_EMU &&
120: changed->System.nDSPType == DSP_TYPE_EMU)
121: return true;
122: #endif
123:
1.1.1.8 root 124: /* did change CPU type? */
125: if (changed->System.nCpuLevel != current->System.nCpuLevel)
126: return true;
127:
1.1.1.5 root 128: #if ENABLE_WINUAE_CPU
1.1.1.6 root 129: /* Did change CPU address mode? */
130: if (changed->System.bAddressSpace24 != current->System.bAddressSpace24)
131: return true;
132:
1.1.1.5 root 133: /* Did change CPU prefetch mode? */
134: if (changed->System.bCompatibleCpu != current->System.bCompatibleCpu)
135: return true;
136:
137: /* Did change CPU cycle exact? */
138: if (changed->System.bCycleExactCpu != current->System.bCycleExactCpu)
139: return true;
140:
141: /* Did change MMU? */
142: if (changed->System.bMMU != current->System.bMMU)
143: return true;
1.1.1.7 root 144:
145: /* Did change FPU? */
146: if (changed->System.n_FPUType != current->System.n_FPUType)
147: return true;
1.1.1.5 root 148: #endif
149:
1.1 root 150: /* Did change size of memory? */
151: if (current->Memory.nMemorySize != changed->Memory.nMemorySize)
1.1.1.3 root 152: return true;
1.1 root 153:
1.1.1.8 root 154: /* MIDI related IRQs start/stop needs reset */
155: if (current->Midi.bEnableMidi != changed->Midi.bEnableMidi)
156: return true;
157:
1.1.1.3 root 158: return false;
1.1 root 159: }
160:
161:
162: /*-----------------------------------------------------------------------*/
163: /**
164: * Copy details back to configuration and perform reset.
165: */
166: void Change_CopyChangedParamsToConfiguration(CNF_PARAMS *current, CNF_PARAMS *changed, bool bForceReset)
167: {
168: bool NeedReset;
1.1.1.3 root 169: bool bReInitGemdosDrive = false;
170: bool bReInitAcsiEmu = false;
171: bool bReInitIDEEmu = false;
172: bool bReInitIoMem = false;
173: bool bScreenModeChange = false;
174: bool bReInitMidi = false;
1.1.1.7 root 175: bool bReInitPrinter = false;
1.1 root 176: bool bFloppyInsert[MAX_FLOPPYDRIVES];
177: int i;
178:
1.1.1.5 root 179: Dprintf("Changes for:\n");
1.1 root 180: /* Do we need to warn user that changes will only take effect after reset? */
181: if (bForceReset)
182: NeedReset = bForceReset;
183: else
184: NeedReset = Change_DoNeedReset(current, changed);
185:
186: /* Do need to change resolution? Need if change display/overscan settings
187: * (if switch between Colour/Mono cause reset later) or toggle statusbar
188: */
189: if (!NeedReset &&
190: (changed->Screen.nForceBpp != current->Screen.nForceBpp
1.1.1.4 root 191: || changed->Screen.bAspectCorrect != current->Screen.bAspectCorrect
192: || changed->Screen.nMaxWidth != current->Screen.nMaxWidth
193: || changed->Screen.nMaxHeight != current->Screen.nMaxHeight
1.1 root 194: || changed->Screen.bAllowOverscan != current->Screen.bAllowOverscan
195: || changed->Screen.bShowStatusbar != current->Screen.bShowStatusbar))
196: {
1.1.1.5 root 197: Dprintf("- screenmode>\n");
1.1.1.3 root 198: bScreenModeChange = true;
1.1 root 199: }
200:
201: /* Did set new printer parameters? */
202: if (changed->Printer.bEnablePrinting != current->Printer.bEnablePrinting
203: || strcmp(changed->Printer.szPrintToFileName,current->Printer.szPrintToFileName))
204: {
1.1.1.5 root 205: Dprintf("- printer>\n");
1.1.1.7 root 206: Printer_UnInit();
207: bReInitPrinter = true;
1.1 root 208: }
209:
210: /* Did set new RS232 parameters? */
211: if (changed->RS232.bEnableRS232 != current->RS232.bEnableRS232
212: || strcmp(changed->RS232.szOutFileName, current->RS232.szOutFileName)
213: || strcmp(changed->RS232.szInFileName, current->RS232.szInFileName))
214: {
1.1.1.5 root 215: Dprintf("- RS-232>\n");
1.1 root 216: RS232_UnInit();
217: }
218:
219: /* Did stop sound? Or change playback Hz. If so, also stop sound recording */
1.1.1.3 root 220: if (!changed->Sound.bEnableSound || changed->Sound.nPlaybackFreq != current->Sound.nPlaybackFreq)
1.1 root 221: {
1.1.1.5 root 222: Dprintf("- sound>\n");
1.1 root 223: if (Sound_AreWeRecording())
224: Sound_EndRecording();
225: Audio_UnInit();
226: }
227:
228: /* Did change floppy (images)? */
229: for (i = 0; i < MAX_FLOPPYDRIVES; i++)
230: {
231: /*
232: Log_Printf(LOG_DEBUG, "Old and new disk %c:\n\t%s\n\t%s", 'A'+i,
233: current->DiskImage.szDiskFileName[i],
234: changed->DiskImage.szDiskFileName[i]);
235: */
236: if (strcmp(changed->DiskImage.szDiskFileName[i],
237: current->DiskImage.szDiskFileName[i])
238: || strcmp(changed->DiskImage.szDiskZipPath[i],
239: current->DiskImage.szDiskZipPath[i]))
1.1.1.3 root 240: bFloppyInsert[i] = true;
1.1 root 241: else
1.1.1.3 root 242: bFloppyInsert[i] = false;
1.1 root 243: }
244:
1.1.1.9 ! root 245: if ( changed->DiskImage.EnableDriveA != current->DiskImage.EnableDriveA )
! 246: FDC_Drive_Set_Enable ( 0 , changed->DiskImage.EnableDriveA );
! 247: if ( changed->DiskImage.EnableDriveB != current->DiskImage.EnableDriveB )
! 248: FDC_Drive_Set_Enable ( 1 , changed->DiskImage.EnableDriveB );
! 249:
! 250: if ( changed->DiskImage.DriveA_NumberOfHeads != current->DiskImage.DriveA_NumberOfHeads )
! 251: FDC_Drive_Set_NumberOfHeads ( 0 , changed->DiskImage.DriveA_NumberOfHeads );
! 252: if ( changed->DiskImage.DriveB_NumberOfHeads != current->DiskImage.DriveB_NumberOfHeads )
! 253: FDC_Drive_Set_NumberOfHeads ( 1 , changed->DiskImage.DriveB_NumberOfHeads );
! 254:
1.1 root 255: /* Did change GEMDOS drive? */
256: if (changed->HardDisk.bUseHardDiskDirectories != current->HardDisk.bUseHardDiskDirectories
257: || (strcmp(changed->HardDisk.szHardDiskDirectories[0], current->HardDisk.szHardDiskDirectories[0])
258: && changed->HardDisk.bUseHardDiskDirectories))
259: {
1.1.1.5 root 260: Dprintf("- gemdos HD>\n");
1.1 root 261: GemDOS_UnInitDrives();
1.1.1.3 root 262: bReInitGemdosDrive = true;
1.1 root 263: }
264:
1.1.1.9 ! root 265: /* Did change ACSI image? */
! 266: for (i = 0; i < MAX_ACSI_DEVS; i++)
1.1 root 267: {
1.1.1.9 ! root 268: if (changed->Acsi[i].bUseDevice != current->Acsi[i].bUseDevice
! 269: || (strcmp(changed->Acsi[i].sDeviceFile, current->Acsi[i].sDeviceFile)
! 270: && changed->Acsi[i].bUseDevice))
! 271: {
! 272: Dprintf("- ACSI image %i>\n", i);
! 273: bReInitAcsiEmu = true;
! 274: }
1.1.1.3 root 275: }
1.1.1.9 ! root 276: if (bReInitAcsiEmu)
! 277: HDC_UnInit();
! 278:
1.1.1.4 root 279: /* Did change IDE HD master image? */
280: if (changed->HardDisk.bUseIdeMasterHardDiskImage != current->HardDisk.bUseIdeMasterHardDiskImage
281: || (strcmp(changed->HardDisk.szIdeMasterHardDiskImage, current->HardDisk.szIdeMasterHardDiskImage)
282: && changed->HardDisk.bUseIdeMasterHardDiskImage))
283: {
1.1.1.5 root 284: Dprintf("- IDE master>\n");
1.1.1.4 root 285: Ide_UnInit();
286: bReInitIDEEmu = true;
287: }
288:
289: /* Did change IDE HD slave image? */
290: if (changed->HardDisk.bUseIdeSlaveHardDiskImage != current->HardDisk.bUseIdeSlaveHardDiskImage
291: || (strcmp(changed->HardDisk.szIdeSlaveHardDiskImage, current->HardDisk.szIdeSlaveHardDiskImage)
292: && changed->HardDisk.bUseIdeSlaveHardDiskImage))
1.1.1.3 root 293: {
1.1.1.5 root 294: Dprintf("- IDE slave>\n");
1.1.1.3 root 295: Ide_UnInit();
296: bReInitIDEEmu = true;
1.1 root 297: }
298:
299: /* Did change blitter, rtc or system type? */
300: if (changed->System.bBlitter != current->System.bBlitter
301: #if ENABLE_DSP_EMU
302: || changed->System.nDSPType != current->System.nDSPType
303: #endif
304: || changed->System.bRealTimeClock != current->System.bRealTimeClock
305: || changed->System.nMachineType != current->System.nMachineType)
306: {
1.1.1.5 root 307: Dprintf("- blitter/rtc/dsp/machine>\n");
1.1 root 308: IoMem_UnInit();
1.1.1.3 root 309: bReInitIoMem = true;
1.1 root 310: }
311:
312: #if ENABLE_DSP_EMU
313: /* Disabled DSP? */
1.1.1.3 root 314: if (current->System.nDSPType == DSP_TYPE_EMU &&
315: changed->System.nDSPType != DSP_TYPE_EMU)
1.1 root 316: {
1.1.1.5 root 317: Dprintf("- DSP>\n");
1.1 root 318: DSP_UnInit();
319: }
320: #endif
321:
1.1.1.2 root 322: /* Did change MIDI settings? */
323: if (current->Midi.bEnableMidi != changed->Midi.bEnableMidi
324: || ((strcmp(changed->Midi.sMidiOutFileName, current->Midi.sMidiOutFileName)
325: || strcmp(changed->Midi.sMidiInFileName, current->Midi.sMidiInFileName))
326: && changed->Midi.bEnableMidi))
327: {
1.1.1.5 root 328: Dprintf("- midi>\n");
1.1.1.2 root 329: Midi_UnInit();
330: bReInitMidi = true;
331: }
332:
1.1 root 333: /* Copy details to configuration,
334: * so it can be saved out or set on reset
335: */
336: if (changed != &ConfigureParams)
337: {
338: ConfigureParams = *changed;
339: }
340:
341: /* Copy details to global, if we reset copy them all */
342: Configuration_Apply(NeedReset);
343:
344: #if ENABLE_DSP_EMU
1.1.1.3 root 345: if (current->System.nDSPType != DSP_TYPE_EMU &&
346: changed->System.nDSPType == DSP_TYPE_EMU)
1.1 root 347: {
1.1.1.5 root 348: Dprintf("- DSP<\n");
1.1 root 349: DSP_Init();
350: }
351: #endif
352:
353: /* Set keyboard remap file */
354: if (ConfigureParams.Keyboard.nKeymapType == KEYMAP_LOADED)
1.1.1.5 root 355: {
356: Dprintf("- keymap<\n");
1.1 root 357: Keymap_LoadRemapFile(ConfigureParams.Keyboard.szMappingFileName);
1.1.1.5 root 358: }
1.1 root 359:
360: /* Mount a new HD image: */
1.1.1.9 ! root 361: if (bReInitAcsiEmu)
1.1 root 362: {
1.1.1.5 root 363: Dprintf("- HD<\n");
1.1.1.4 root 364: HDC_Init();
1.1 root 365: }
366:
1.1.1.4 root 367: /* Mount a new IDE HD master or slave image: */
368: if (bReInitIDEEmu && (ConfigureParams.HardDisk.bUseIdeMasterHardDiskImage || ConfigureParams.HardDisk.bUseIdeSlaveHardDiskImage))
1.1.1.3 root 369: {
1.1.1.5 root 370: Dprintf("- IDE<\n");
1.1.1.3 root 371: Ide_Init();
372: }
373:
1.1 root 374: /* Insert floppies? */
375: for (i = 0; i < MAX_FLOPPYDRIVES; i++)
376: {
377: if (bFloppyInsert[i])
1.1.1.5 root 378: {
379: Dprintf("- floppy<\n");
1.1 root 380: Floppy_InsertDiskIntoDrive(i);
1.1.1.5 root 381: }
1.1 root 382: }
383:
384: /* Mount a new GEMDOS drive? */
385: if (bReInitGemdosDrive && ConfigureParams.HardDisk.bUseHardDiskDirectories)
386: {
1.1.1.5 root 387: Dprintf("- gemdos HD<\n");
1.1 root 388: GemDOS_InitDrives();
389: }
390:
391: /* Restart audio sub system if necessary: */
392: if (ConfigureParams.Sound.bEnableSound && !bSoundWorking)
393: {
1.1.1.5 root 394: Dprintf("- audio<\n");
1.1 root 395: Audio_Init();
396: }
397:
398: /* Re-initialize the RS232 emulation: */
1.1.1.7 root 399: if (ConfigureParams.RS232.bEnableRS232)
1.1 root 400: {
1.1.1.5 root 401: Dprintf("- RS-232<\n");
1.1 root 402: RS232_Init();
403: }
404:
405: /* Re-init IO memory map? */
406: if (bReInitIoMem)
407: {
1.1.1.5 root 408: Dprintf("- IO mem<\n");
1.1 root 409: IoMem_Init();
410: }
411:
1.1.1.7 root 412: /* Re-init Printer emulation? */
413: if (bReInitPrinter)
414: {
415: Dprintf("- printer<\n");
416: Printer_Init();
417: }
418:
1.1.1.2 root 419: /* Re-init MIDI emulation? */
420: if (bReInitMidi)
421: {
1.1.1.5 root 422: Dprintf("- midi<\n");
1.1.1.2 root 423: Midi_Init();
424: }
425:
1.1 root 426: /* Force things associated with screen change */
427: if (bScreenModeChange)
428: {
1.1.1.5 root 429: Dprintf("- screenmode<\n");
1.1 root 430: Screen_ModeChanged();
431: }
432:
433: /* Do we need to perform reset? */
434: if (NeedReset)
435: {
1.1.1.5 root 436: Dprintf("- reset\n");
1.1 root 437: Reset_Cold();
438: }
439:
440: /* Go into/return from full screen if flagged */
441: if (!bInFullScreen && ConfigureParams.Screen.bFullScreen)
442: Screen_EnterFullScreen();
443: else if (bInFullScreen && !ConfigureParams.Screen.bFullScreen)
444: Screen_ReturnFromFullScreen();
1.1.1.4 root 445:
446: /* update statusbar info (CPU, MHz, mem etc) */
447: Statusbar_UpdateInfo();
1.1.1.5 root 448: Dprintf("done.\n");
1.1 root 449: }
450:
451:
452: /*-----------------------------------------------------------------------*/
453: /**
454: * Change given Hatari options
1.1.1.3 root 455: * Return false if parsing failed, true otherwise
1.1 root 456: */
457: static bool Change_Options(int argc, const char *argv[])
458: {
459: bool bOK;
460: CNF_PARAMS current;
461:
1.1.1.3 root 462: Main_PauseEmulation(false);
1.1 root 463:
464: /* get configuration changes */
465: current = ConfigureParams;
466: ConfigureParams.Screen.bFullScreen = bInFullScreen;
467: bOK = Opt_ParseParameters(argc, argv);
468:
469: /* Check if reset is required and ask user if he really wants to continue */
470: if (bOK && Change_DoNeedReset(¤t, &ConfigureParams)
1.1.1.5 root 471: && current.Log.nAlertDlgLogLevel > LOG_FATAL) {
1.1 root 472: bOK = DlgAlert_Query("The emulated system must be "
473: "reset to apply these changes. "
474: "Apply changes now and reset "
475: "the emulator?");
476: }
477: /* Copy details to configuration */
478: if (bOK) {
1.1.1.3 root 479: Change_CopyChangedParamsToConfiguration(¤t, &ConfigureParams, false);
1.1 root 480: } else {
481: ConfigureParams = current;
482: }
483:
484: Main_UnPauseEmulation();
485: return bOK;
486: }
487:
488:
489: /*-----------------------------------------------------------------------*/
490: /**
1.1.1.7 root 491: * Parse given command line and change Hatari options accordingly.
492: * Given string must be stripped and not empty.
1.1.1.3 root 493: * Return false if parsing failed or there were no args, true otherwise
1.1 root 494: */
495: bool Change_ApplyCommandline(char *cmdline)
496: {
497: int i, argc, inarg;
498: const char **argv;
499: bool ret;
500:
501: /* count args */
502: inarg = argc = 0;
503: for (i = 0; cmdline[i]; i++)
504: {
1.1.1.9 ! root 505: if (isspace((unsigned char)cmdline[i]) && cmdline[i-1] != '\\')
1.1 root 506: {
507: inarg = 0;
508: continue;
509: }
510: if (!inarg)
511: {
512: inarg++;
513: argc++;
514: }
515: }
516: if (!argc)
517: {
1.1.1.3 root 518: return false;
1.1 root 519: }
520: /* 2 = "hatari" + NULL */
521: argv = malloc((argc+2) * sizeof(char*));
522: if (!argv)
523: {
524: perror("command line alloc");
1.1.1.3 root 525: return false;
1.1 root 526: }
527:
528: /* parse them to array */
529: fprintf(stderr, "Command line with '%d' arguments:\n", argc);
530: inarg = argc = 0;
531: argv[argc++] = "hatari";
532: for (i = 0; cmdline[i]; i++)
533: {
1.1.1.9 ! root 534: if (isspace((unsigned char)cmdline[i]))
1.1 root 535: {
1.1.1.7 root 536: if (cmdline[i-1] != '\\')
1.1 root 537: {
1.1.1.7 root 538: cmdline[i] = '\0';
539: if (inarg)
540: {
541: fprintf(stderr, "- '%s'\n", argv[argc-1]);
542: }
543: inarg = 0;
544: continue;
545: }
546: else
547: {
548: /* remove quote for space */
549: memcpy(cmdline+i-1, cmdline+i, strlen(cmdline+i)+1);
550: i--;
1.1 root 551: }
552: }
553: if (!inarg)
554: {
555: argv[argc++] = &(cmdline[i]);
556: inarg++;
557: }
558: }
559: if (inarg)
560: {
561: fprintf(stderr, "- '%s'\n", argv[argc-1]);
562: }
563: argv[argc] = NULL;
564:
565: /* do args */
566: ret = Change_Options(argc, argv);
1.1.1.3 root 567: free((void *)argv);
1.1 root 568: return ret;
569: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.