|
|
1.1 ! root 1: /* ! 2: Hatari - inffile.c ! 3: ! 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. ! 6: ! 7: TOS *.INF file overloading for autostarting & TOS resolution overriding. ! 8: */ ! 9: const char INFFILE_fileid[] = "Hatari inffile.c : " __DATE__ " " __TIME__; ! 10: ! 11: #include <SDL_endian.h> ! 12: #include <assert.h> ! 13: #include <errno.h> ! 14: ! 15: #include "main.h" ! 16: #include "configuration.h" ! 17: #include "inffile.h" ! 18: #include "options.h" ! 19: #include "gemdos.h" ! 20: #include "file.h" ! 21: #include "log.h" ! 22: #include "str.h" ! 23: #include "tos.h" ! 24: #include "vdi.h" ! 25: ! 26: /* debug output + leaves virtual INF file behind */ ! 27: #define INF_DEBUG 0 ! 28: ! 29: /* TOS resolution numbers used in Atari TOS INF files. ! 30: * ! 31: * Note: EmuTOS uses different numbers, so these are re-mapped. ! 32: */ ! 33: typedef enum { ! 34: RES_UNSET, /* 0 */ ! 35: RES_ST_LOW, /* 1 */ ! 36: RES_ST_MED, /* 2 */ ! 37: RES_ST_HIGH, /* 3 */ ! 38: RES_TT_MED, /* 4, also Falcon 80 cols mode */ ! 39: RES_TT_HIGH, /* 5 */ ! 40: RES_TT_LOW, /* 6, also Falcon 40 cols mode */ ! 41: RES_COUNT ! 42: } res_value_t; ! 43: ! 44: ! 45: static struct { ! 46: FILE *file; /* file pointer to contents of INF file */ ! 47: char *prgname; /* TOS name of the program to auto start */ ! 48: const char *infname; /* name of the INF file TOS will try to match */ ! 49: res_value_t reso; /* resolution setting value request for #E line */ ! 50: /* for validation */ ! 51: int reso_id; ! 52: const char *reso_str; ! 53: int prgname_id; ! 54: } TosOverride; ! 55: ! 56: ! 57: /* autostarted program name will be added before the first ! 58: * '@' character in the INF files #Z line ! 59: * (first value is 00: TOS, 01: GEM). ! 60: * ! 61: * Resolution is specified in the 2nd hex value in #E line ! 62: * in normal TOS and EmuTOS <= 0.9.6, and in 4th hex value ! 63: * in EmuTOS >= 0.9.7. Hatari supports only latter EmuTOS ! 64: * versions. ! 65: * ! 66: * TOS versions expect both of these to be within certain ! 67: * number of bytes from the beginning of the file, and there ! 68: * are also TOS version specific limits on the INF file sizes. ! 69: * ! 70: * More documentation on the DESKTOP.INF file content: ! 71: * http://st-news.com/issues/st-news-volume-2-issue-6/education/the-desktopinf-file/ ! 72: */ ! 73: ! 74: /* EmuDesk INF file format and values differ from normal TOS */ ! 75: static const char emudesk_inf[] = ! 76: "#R 01\r\n" ! 77: "#E 1A 61 FF 00 00\r\n" ! 78: "#W 00 00 02 08 26 0C 00 @\r\n" ! 79: "#W 00 00 02 0A 26 0C 00 @\r\n" ! 80: "#W 00 00 02 0D 26 0C 00 @\r\n" ! 81: "#W 00 00 00 00 14 0B 00 @\r\n" ! 82: "#W 00 00 00 00 14 0B 00 @\r\n" ! 83: "#W 00 00 00 00 14 0B 00 @\r\n" ! 84: "#M 00 00 01 FF A DISK A@ @\r\n" ! 85: "#M 01 00 01 FF B DISK B@ @\r\n" ! 86: "#M 02 00 00 FF C DISK C@ @\r\n" ! 87: "#F FF 07 @ *.*@ 000 @\r\n" ! 88: "#N FF 07 @ *.*@ 000 @\r\n" ! 89: "#D FF 02 @ *.*@\r\n" ! 90: "#Y 06 FF *.GTP@ @ 000 @\r\n" ! 91: "#G 06 FF *.APP@ @ 000 @\r\n" ! 92: "#G 06 FF *.PRG@ @ 000 @\r\n" ! 93: "#P 06 FF *.TTP@ @ 000 @\r\n" ! 94: "#F 06 FF *.TOS@ @ 000 @\r\n" ! 95: "#T 00 03 03 FF TRASH@ @\r\n"; ! 96: ! 97: /* TOS v1.04 works only with DESKTOP.INF from that version ! 98: * (it crashes with newer INF after autobooted program exits), ! 99: * later v1.x TOS versions work also with this. ! 100: * ! 101: * Trailing spaces are significant for TOS parsing. ! 102: */ ! 103: static const char desktop_inf[] = ! 104: "#a000000\r\n" ! 105: "#b000000\r\n" ! 106: "#c7770007000600070055200505552220770557075055507703111103\r\n" ! 107: "#d \r\n" ! 108: "#E 18 11 \r\n" ! 109: "#W 00 00 02 0B 26 09 00 @\r\n" ! 110: "#W 00 00 0A 0F 1A 09 00 @\r\n" ! 111: "#W 00 00 0E 01 1A 09 00 @\r\n" ! 112: "#M 01 00 00 FF C HARD DISK@ @ \r\n" ! 113: "#M 00 00 00 FF A FLOPPY DISK@ @ \r\n" ! 114: "#M 00 01 00 FF B FLOPPY DISK@ @ \r\n" ! 115: "#T 00 03 02 FF TRASH@ @ \r\n" ! 116: "#F FF 04 @ *.*@ \r\n" ! 117: "#D FF 01 @ *.*@ \r\n" ! 118: "#G 03 FF *.APP@ @ \r\n" ! 119: "#G 03 FF *.PRG@ @ \r\n" ! 120: "#P 03 FF *.TTP@ @ \r\n" ! 121: "#F 03 04 *.TOS@ @ \r\n" ! 122: "\032"; ! 123: ! 124: /* TOS v2.x and newer have also different format, using ! 125: * TOS v1.04 INF file would result in bogus resolution with TOS v4 ! 126: */ ! 127: static const char newdesk_inf[] = ! 128: "#a000000\r\n" ! 129: "#b000000\r\n" ! 130: "#c7770007000600070055200505552220770557075055507703111103\r\n" ! 131: "#d \r\n" ! 132: "#K 4F 53 4C 00 46 42 43 57 45 58 00 00 00 00 00 00 00 00 00 00 00 00 00 52 00 00 4D 56 50 00 @\r\n" ! 133: "#E 18 01 00 06 \r\n" ! 134: "#Q 41 40 43 40 43 40 \r\n" ! 135: "#W 00 00 02 0B 26 09 00 @\r\n" ! 136: "#W 00 00 0A 0F 1A 09 00 @\r\n" ! 137: "#W 00 00 0E 01 1A 09 00 @\r\n" ! 138: "#W 00 00 04 07 26 0C 00 @\r\n" ! 139: "#W 00 00 0C 0B 26 09 00 @\r\n" ! 140: "#W 00 00 08 0F 1A 09 00 @\r\n" ! 141: "#W 00 00 06 01 1A 09 00 @\r\n" ! 142: "#N FF 04 000 @ *.*@ @ \r\n" ! 143: "#D FF 01 000 @ *.*@ @ \r\n" ! 144: "#G 03 FF 000 *.APP@ @ @ \r\n" ! 145: "#G 03 FF 000 *.PRG@ @ @ \r\n" ! 146: "#Y 03 FF 000 *.GTP@ @ @ \r\n" ! 147: "#P 03 FF 000 *.TTP@ @ @ \r\n" ! 148: "#F 03 04 000 *.TOS@ @ @ \r\n" ! 149: "#M 00 01 00 FF C HARD DISK@ @ \r\n" ! 150: "#M 00 00 00 FF A FLOPPY DISK@ @ \r\n" ! 151: "#M 01 00 00 FF B FLOPPY DISK@ @ \r\n" ! 152: "#T 00 03 02 FF TRASH@ @ \r\n"; ! 153: ! 154: ! 155: /*-----------------------------------------------------------------------*/ ! 156: /** ! 157: * Set name of program that will be auto started after TOS boots. ! 158: * Supported only from TOS 1.04 forward. ! 159: * ! 160: * If program lacks a path, "C:\" will be added. ! 161: * ! 162: * Returns true if OK, false for obviously invalid path specification. ! 163: */ ! 164: bool INF_SetAutoStart(const char *name, int opt_id) ! 165: { ! 166: char *prgname; ! 167: int len = strlen(name); ! 168: char drive = toupper(name[0]); ! 169: ! 170: if (drive >= 'A' && drive <= 'Z' && name[1] == ':') ! 171: { ! 172: /* full path */ ! 173: const char *ptr; ! 174: int offset; ! 175: prgname = malloc(len+2); ! 176: ptr = strrchr(name, '\\'); ! 177: if (ptr) ! 178: offset = ptr - name + 1; ! 179: else ! 180: offset = 2; ! 181: /* copy/upcase path part */ ! 182: memcpy(prgname, name, offset); ! 183: prgname[offset] = '\0'; ! 184: Str_ToUpper(prgname); ! 185: ! 186: if (name[2] != '\\') ! 187: { ! 188: /* NOT OK: A:DIR\NAME.PRG */ ! 189: if (ptr) ! 190: { ! 191: free(prgname); ! 192: return false; ! 193: } ! 194: /* A:NAME.PRG -> A:\NAME.PRG */ ! 195: prgname[offset] = '\\'; ! 196: /* copy/upcase file part */ ! 197: Str_Filename2TOSname(name+offset, prgname+offset+1); ! 198: } else { ! 199: /* copy/upcase file part */ ! 200: Str_Filename2TOSname(name+offset, prgname+offset); ! 201: } ! 202: } ! 203: else if (strchr(name, '\\')) ! 204: { ! 205: /* partial path not accepted */ ! 206: return false; ! 207: } ! 208: else ! 209: { ! 210: /* just program -> add path */ ! 211: prgname = malloc(3 + len + 1); ! 212: strcpy(prgname, "C:\\"); ! 213: Str_Filename2TOSname(name, prgname+3); ! 214: } ! 215: if (TosOverride.prgname) ! 216: free(TosOverride.prgname); ! 217: TosOverride.prgname = prgname; ! 218: TosOverride.prgname_id = opt_id; ! 219: return true; ! 220: } ! 221: ! 222: ! 223: /*-----------------------------------------------------------------------*/ ! 224: /** ! 225: * Set specified TOS resolution override. ! 226: * ! 227: * Return true for success, false otherwise. ! 228: */ ! 229: bool INF_SetResolution(const char *str, int opt_id) ! 230: { ! 231: int reso; ! 232: ! 233: /* map to values used by real TOS INF files */ ! 234: if (strcmp(str, "low") == 0) ! 235: reso = RES_ST_LOW; ! 236: else if (strcmp(str, "med") == 0) ! 237: reso = RES_ST_MED; ! 238: else if (strcmp(str, "high") == 0) ! 239: reso = RES_ST_HIGH; ! 240: else if (strcmp(str, "ttmed") == 0) ! 241: reso = RES_TT_MED; ! 242: else if (strcmp(str, "ttlow") == 0) ! 243: reso = RES_TT_LOW; ! 244: else ! 245: { ! 246: reso = atoi(str); ! 247: if (reso <= RES_UNSET || reso >= RES_COUNT) ! 248: return false; ! 249: } ! 250: TosOverride.reso = reso; ! 251: TosOverride.reso_id = opt_id; ! 252: TosOverride.reso_str = str; ! 253: return true; ! 254: } ! 255: ! 256: ! 257: /*-----------------------------------------------------------------------*/ ! 258: /** ! 259: * Validate autostart options against Hatari settings: ! 260: * - program drive ! 261: * ! 262: * If there's a problem, return problematic option ID ! 263: * and set val & err strings, otherwise just return zero. ! 264: */ ! 265: int INF_ValidateAutoStart(const char **val, const char **err) ! 266: { ! 267: const char *path = TosOverride.prgname; ! 268: char drive; ! 269: ! 270: if (!path) ! 271: return 0; ! 272: ! 273: /* validate autostart program drive */ ! 274: drive = path[0]; ! 275: ! 276: if (drive == 'A') ! 277: { ! 278: if (ConfigureParams.DiskImage.EnableDriveA && ConfigureParams.DiskImage.szDiskFileName[0][0]) ! 279: return 0; ! 280: } ! 281: else if (drive == 'B') ! 282: { ! 283: if (ConfigureParams.DiskImage.EnableDriveB && ConfigureParams.DiskImage.szDiskFileName[1][0]) ! 284: return 0; ! 285: } ! 286: /* exact drive checking for hard drives would require: ! 287: * ! 288: * For images: ! 289: * - finding out what partitions each of the IDE master & ! 290: * Slave, 8 ACSI, and 8 SCSI images do have, *and* ! 291: * - finding out which of those partitions the native Atari ! 292: * harddisk driver happens to support... ! 293: * -> not feasible ! 294: * ! 295: * For GEMDOS HD: ! 296: * - If multiple partitions are specified, which ones ! 297: * - If not, what is the single partition drive letter ! 298: * ! 299: * So, just check that some harddisk is enabled for C: -> ! 300: */ ! 301: ! 302: /* GEMDOS HD */ ! 303: else if (ConfigureParams.HardDisk.bUseHardDiskDirectories && ConfigureParams.HardDisk.szHardDiskDirectories[0][0]) ! 304: { ! 305: return 0; ! 306: } ! 307: /* IDE */ ! 308: else if (ConfigureParams.HardDisk.bUseIdeMasterHardDiskImage && ConfigureParams.HardDisk.szIdeMasterHardDiskImage[0]) ! 309: { ! 310: return 0; ! 311: } ! 312: else if (ConfigureParams.HardDisk.bUseIdeMasterHardDiskImage && ConfigureParams.HardDisk.szIdeMasterHardDiskImage[0]) ! 313: { ! 314: return 0; ! 315: } ! 316: else ! 317: { ! 318: /* ACSI / SCSI */ ! 319: int i; ! 320: for (i = 0; i < MAX_ACSI_DEVS; i++) ! 321: { ! 322: if (ConfigureParams.Acsi[i].bUseDevice && ConfigureParams.Acsi[i].sDeviceFile[0]) ! 323: return 0; ! 324: if (ConfigureParams.Scsi[i].bUseDevice && ConfigureParams.Scsi[i].sDeviceFile[0]) ! 325: return 0; ! 326: } ! 327: } ! 328: /* error */ ! 329: *val = TosOverride.prgname; ! 330: *err = "Required autostart drive isn't enabled"; ! 331: return TosOverride.prgname_id; ! 332: } ! 333: ! 334: ! 335: /** ! 336: * Map / set VDI to INF file resolution ! 337: */ ! 338: static res_value_t vdi2inf(res_value_t mode) ! 339: { ! 340: res_value_t res = TosOverride.reso; ! 341: res_value_t newres = mode + 1; ! 342: if (res != newres) ! 343: { ! 344: if (res) ! 345: Log_Printf(LOG_WARN, "Overriding TOS resolution %d with VDI resolution %d.\n", ! 346: res, newres); ! 347: res = newres; ! 348: } ! 349: return res; ! 350: } ! 351: ! 352: /** ! 353: * Map / set VDI to INF file resolution ! 354: */ ! 355: extern void INF_SetVdiMode(int vdi_res) ! 356: { ! 357: TosOverride.reso = vdi2inf(vdi_res); ! 358: } ! 359: ! 360: ! 361: /** ! 362: * Resolution needs to be validated later, here, because we don't ! 363: * know the final machine type when options are parsed, as it can ! 364: * change later when TOS is loaded. ! 365: * ! 366: * Resolution settings are: ! 367: * 0: no override ! 368: * 1-3: ST/STE resolutions: ! 369: * - ST low, med, high ! 370: * 4-6: TT/Falcon resolutions: ! 371: * - TT med, high, low ! 372: * - Falcon 80 cols, N/A, 40 cols ! 373: * ! 374: * If there's a problem, return problematic option ID ! 375: * and set val & err strings, otherwise just return zero. ! 376: */ ! 377: static int INF_ValidateResolution(int *set_res, const char **val, const char **err) ! 378: { ! 379: res_value_t res = TosOverride.reso; ! 380: *set_res = 0; ! 381: ! 382: /* VDI resolution overrides TOS resolution setting */ ! 383: if (bUseVDIRes) ! 384: { ! 385: res = vdi2inf(VDIRes); ! 386: } ! 387: else ! 388: { ! 389: int monitor = ConfigureParams.Screen.nMonitorType; ! 390: ! 391: /* validate given TOS resolution */ ! 392: if (!res) ! 393: return 0; ! 394: ! 395: *val = TosOverride.reso_str; ! 396: ! 397: switch(ConfigureParams.System.nMachineType) ! 398: { ! 399: case MACHINE_STE: ! 400: case MACHINE_MEGA_STE: ! 401: case MACHINE_ST: ! 402: case MACHINE_MEGA_ST: ! 403: if (monitor == MONITOR_TYPE_MONO && res != RES_ST_HIGH) ! 404: { ! 405: res = RES_ST_HIGH; ! 406: Log_Printf(LOG_WARN, "With mono monitor, TOS can use only mono resolution, correcting.\n"); ! 407: } ! 408: else if (res >= RES_ST_HIGH) ! 409: { ! 410: *err = "invalid ST/STE color TOS resolution"; ! 411: return TosOverride.reso_id; ! 412: } ! 413: break; ! 414: ! 415: case MACHINE_TT: ! 416: if (monitor == MONITOR_TYPE_MONO && res != RES_TT_HIGH) ! 417: { ! 418: res = RES_TT_HIGH; ! 419: Log_Printf(LOG_WARN, "With mono monitor, TOS can use only mono resolution, correcting.\n"); ! 420: } ! 421: else if (res == RES_TT_HIGH) ! 422: { ! 423: *err = "invalid TT color TOS resolution"; ! 424: return TosOverride.reso_id; ! 425: } ! 426: break; ! 427: ! 428: case MACHINE_FALCON: ! 429: if (monitor == MONITOR_TYPE_MONO && res != RES_ST_HIGH) ! 430: { ! 431: res = RES_ST_HIGH; ! 432: Log_Printf(LOG_WARN, "With mono monitor, TOS can use only mono resolution, correcting.\n"); ! 433: } ! 434: else if (res == RES_TT_HIGH) ! 435: { ! 436: *err = "TT-mono is invalid Falcon TOS resolution"; ! 437: return TosOverride.reso_id; ! 438: } ! 439: else ! 440: { ! 441: Log_Printf(LOG_WARN, "TOS resolution setting doesn't work with Falcon (yet)\n"); ! 442: } ! 443: /* TODO: ! 444: * Falcon resolution setting doesn't have effect, ! 445: * seems that #E Falcon settings in columns 6 & 7 ! 446: * are also needed: ! 447: * - line doubling / interlace ! 448: * - ST compat, RGB/VGA, columns & #colors ! 449: */ ! 450: break; ! 451: } ! 452: } ! 453: ! 454: if (bIsEmuTOS) ! 455: { ! 456: /* map values 0-6: N/A, ST low, med, high, TT med, high, low */ ! 457: unsigned char map[] = { 0, 0, 1, 2, 4, 6, 7 }; ! 458: res = map[res]; ! 459: Log_Printf(LOG_INFO, "Remapped TOS resolution for EmuTOS.\n"); ! 460: } ! 461: else if (TosVersion >= 0x0160) ! 462: { ! 463: switch(ConfigureParams.System.nMachineType) ! 464: { ! 465: case MACHINE_STE: ! 466: case MACHINE_MEGA_STE: ! 467: case MACHINE_FALCON: ! 468: /* enable blitter */ ! 469: res |= 0x10; ! 470: break; ! 471: default: ! 472: break; ! 473: } ! 474: } ! 475: ! 476: Log_Printf(LOG_INFO, "Resulting INF file TOS resolution: 0x%02x -> 0x%02x.\n", TosOverride.reso, res); ! 477: *set_res = res; ! 478: return 0; ! 479: } ! 480: ! 481: ! 482: /*-----------------------------------------------------------------------*/ ! 483: /** ! 484: * Get builtin INF file contents which open window for the boot drive, ! 485: * if any. ! 486: * ! 487: * NOTE: this won't work for EmuTOS because it opens INF file second ! 488: * time to read window info, at which point the temporary virtual INF ! 489: * file has already disappeared. Real TOS versions read INF file ! 490: * only once and work fine. ! 491: */ ! 492: static char *get_builtin_inf(const char *contents) ! 493: { ! 494: /* line to open window (for boot drive) */ ! 495: static const char drivewin[] = "#W 00 00 02 06 26 0C 00 X:\\*.*@\r\n"; ! 496: int winlen, inflen, winoffset1, winoffset2, driveoffset; ! 497: const char *winline; ! 498: char *inf; ! 499: ! 500: assert(contents); ! 501: ! 502: inflen = strlen(contents); ! 503: winlen = strlen(drivewin); ! 504: inf = malloc(inflen + winlen + 1); ! 505: assert(inf); ! 506: ! 507: /* drive letter offset on drive window line */ ! 508: driveoffset = strchr(drivewin, 'X') - drivewin; ! 509: ! 510: /* first copy everything until first window line */ ! 511: winline = strstr(contents, "#W"); ! 512: assert(winline); ! 513: winoffset2 = winoffset1 = winline - contents; ! 514: memcpy(inf, contents, winoffset1); ! 515: ! 516: /* then comes boot drive window line, if any */ ! 517: if (ConfigureParams.HardDisk.bBootFromHardDisk) ! 518: { ! 519: /* C:, ignore IDE/ACSI for now */ ! 520: if (GemDOS_IsDriveEmulated(2)) ! 521: { ! 522: strcpy(inf + winoffset1, drivewin); ! 523: inf[winoffset1 + driveoffset] = 'C'; ! 524: winoffset2 += winlen; ! 525: } ! 526: } ! 527: else if (ConfigureParams.DiskImage.EnableDriveA && ConfigureParams.DiskImage.szDiskFileName[0][0]) ! 528: { ! 529: /* A: */ ! 530: strcpy(inf + winoffset1, drivewin); ! 531: inf[winoffset1 + driveoffset] = 'A'; ! 532: winoffset2 += winlen; ! 533: } ! 534: /* finally copy rest */ ! 535: strcpy(inf + winoffset2, contents + winoffset1); ! 536: ! 537: return inf; ! 538: } ! 539: ! 540: /** ! 541: * Get suitable Atari desktop configuration file for current TOS version, ! 542: * either by loading existing file, or creating default one if there isn't ! 543: * a pre-existing one. ! 544: * ! 545: * Return INF file contents and set its name & size to args. ! 546: */ ! 547: static char *get_inf_file(const char **set_infname, int *set_size, int *res_col) ! 548: { ! 549: char *hostname; ! 550: const char *contents, *infname; ! 551: Uint8 *host_content; ! 552: long host_size; ! 553: int size; ! 554: ! 555: /* default position of the 2 digit hex code for resolution on #E line */ ! 556: *res_col = 6; ! 557: ! 558: /* infname needs to be exactly the same string that given ! 559: * TOS version gives for GEMDOS to find. ! 560: */ ! 561: if (bIsEmuTOS) ! 562: { ! 563: if (ConfigureParams.HardDisk.bBootFromHardDisk) ! 564: infname = "C:\\EMUDESK.INF"; ! 565: else ! 566: infname = "A:\\EMUDESK.INF"; ! 567: size = sizeof(emudesk_inf); ! 568: contents = emudesk_inf; ! 569: *res_col = 12; ! 570: } ! 571: /* need to match file TOS searches first */ ! 572: else if (TosVersion >= 0x0200) ! 573: { ! 574: infname = "NEWDESK.INF"; ! 575: size = sizeof(newdesk_inf); ! 576: contents = newdesk_inf; ! 577: } ! 578: else ! 579: { ! 580: infname = "DESKTOP.INF"; ! 581: size = sizeof(desktop_inf); ! 582: contents = desktop_inf; ! 583: } ! 584: *set_infname = infname; ! 585: *set_size = size; ! 586: ! 587: /* Existing INF can be modified only through GEMDOS hard disk, ! 588: * i.e. boot needs to be from C:, which needs to be GEMDOS HD ! 589: */ ! 590: if (!(ConfigureParams.HardDisk.bBootFromHardDisk && GemDOS_IsDriveEmulated(2))) ! 591: { ! 592: #if INF_DEBUG ! 593: fprintf(stderr, "No GEMDOS HD boot drive, using builtin INF autostart file.\n"); ! 594: #endif ! 595: return get_builtin_inf(contents); ! 596: } ! 597: ! 598: hostname = malloc(FILENAME_MAX); ! 599: assert(hostname); ! 600: ! 601: /* convert to host file name, and read that */ ! 602: GemDOS_CreateHardDriveFileName(2, infname, hostname, FILENAME_MAX); ! 603: #if INF_DEBUG ! 604: GemDOS_Info(stderr, 0); ! 605: fprintf(stderr, "\nChecking for existing INF file '%s' -> '%s'...\n", infname, hostname); ! 606: #endif ! 607: host_content = File_Read(hostname, &host_size, NULL); ! 608: ! 609: if (host_content) ! 610: { ! 611: Log_Printf(LOG_INFO, "Going to modify '%s'.\n", hostname); ! 612: free(hostname); ! 613: *set_size = host_size; ! 614: return (char *)host_content; ! 615: } ! 616: Log_Printf(LOG_INFO, "Using builtin '%s'.\n", infname); ! 617: free(hostname); ! 618: return get_builtin_inf(contents); ! 619: } ! 620: ! 621: ! 622: /*-----------------------------------------------------------------------*/ ! 623: /** ! 624: * Skip rest of INF file line. ! 625: * Return index after its end, or zero for error. ! 626: */ ! 627: static int skip_line(const char *contents, int offset, int size) ! 628: { ! 629: int orig = offset; ! 630: char chr; ! 631: ! 632: for (; offset < size; offset++) ! 633: { ! 634: chr = contents[offset]; ! 635: if (chr == '\r' || chr == '\n') ! 636: { ! 637: chr = contents[++offset]; ! 638: if (chr == '\r' || chr == '\n') ! 639: offset++; ! 640: return offset; ! 641: } ! 642: } ! 643: Log_Printf(LOG_WARN, "Malformed INF file '%s', no line end at offsets %d-%d!\n", ! 644: TosOverride.prgname, orig, offset); ! 645: return 0; ! 646: } ! 647: ! 648: /** ! 649: * Return INF file autostart line format suitable for given ! 650: * program type, based on program name extension. ! 651: */ ! 652: static const char *prg_format(const char *prgname) ! 653: { ! 654: const char *ext; ! 655: int size; ! 656: ! 657: size = strlen(prgname); ! 658: if (size > 4) ! 659: ext = prgname + size - 4; ! 660: else ! 661: ext = prgname; ! 662: ! 663: if (strcmp(ext, ".TTP") == 0 ||strcmp(ext, ".TOS") == 0) ! 664: return "#Z 00 %s@ \r\n"; /* TOS program */ ! 665: else ! 666: return "#Z 01 %s@ \r\n"; /* GEM program */ ! 667: } ! 668: ! 669: /** ! 670: * Create modified, temporary INF file that contains the required ! 671: * autostart and resolution information. ! 672: * ! 673: * Return FILE* pointer to it. ! 674: */ ! 675: static FILE* write_inf_file(const char *contents, int size, int res, int res_col) ! 676: { ! 677: const char *infname, *prgname, *format = NULL; ! 678: int offset, off_prg, off_rez, endcheck; ! 679: FILE *fp; ! 680: ! 681: #if defined(WIN32) /* unfortunately tmpfile() needs administrative privileges on windows, so this needs special care */ ! 682: char *ptr = WinTmpFile(); ! 683: if (ptr != NULL) ! 684: fp = fopen(ptr,"w+b"); ! 685: else ! 686: fp = NULL; ! 687: #else ! 688: # if INF_DEBUG ! 689: { ! 690: const char *debugfile = "/tmp/hatari-desktop-inf.txt"; ! 691: fprintf(stderr, "Virtual INF file: '%s'\n", debugfile); ! 692: fp = fopen(debugfile, "w+b"); ! 693: } ! 694: # else ! 695: fp = tmpfile(); ! 696: # endif ! 697: #endif ! 698: prgname = TosOverride.prgname; ! 699: infname = TosOverride.infname; ! 700: ! 701: if (!fp) ! 702: { ! 703: Log_Printf(LOG_ERROR, "Failed to create virtual INF file '%s': %s!\n", ! 704: infname, strerror(errno)); ! 705: return NULL; ! 706: } ! 707: ! 708: if (prgname) ! 709: format = prg_format(prgname); ! 710: ! 711: /* need to fit at least 2 res digits + \r\n */ ! 712: endcheck = size-res_col-2-2; ! 713: /* positions after prog info & resolution info */ ! 714: off_prg = off_rez = 0; ! 715: /* find where to insert the program name and resolution */ ! 716: for (offset = 0; offset < endcheck; offset++) ! 717: { ! 718: if (contents[offset] != '#') ! 719: continue; ! 720: ! 721: /* replace autostart line only when requested */ ! 722: if (prgname && contents[offset+1] == 'Z') ! 723: { ! 724: fwrite(contents+off_prg, offset-off_prg, 1, fp); ! 725: /* write only first #Z line, skip rest */ ! 726: if (!off_prg) ! 727: fprintf(fp, format, prgname); ! 728: offset = skip_line(contents, offset, size-1); ! 729: if (!offset) ! 730: break; ! 731: off_prg = offset; ! 732: } ! 733: /* resolution line always written */ ! 734: if (contents[offset+1] == 'E') ! 735: { ! 736: fwrite(contents+off_prg, offset-off_prg, 1, fp); ! 737: /* INF file with autostart line missing? ! 738: * ! 739: * It's assumed that #Z is always before #E, ! 740: * if it exits. So write one when requested, ! 741: * if it hasn't been written yet. ! 742: */ ! 743: if (prgname && !off_prg) ! 744: { ! 745: off_prg = offset; ! 746: fprintf(fp, format, prgname); ! 747: } ! 748: /* write #E line start */ ! 749: fwrite(contents+offset, res_col, 1, fp); ! 750: /* write requested resolution, or default? ! 751: * ! 752: * (TosOverride.reso tells if there's request, ! 753: * 'res' tells the actual value to use) ! 754: */ ! 755: if (TosOverride.reso) ! 756: fprintf(fp, "%02x", res); ! 757: else ! 758: fwrite(contents+offset+res_col, 2, 1, fp); ! 759: /* set point to rest of #E */ ! 760: offset += res_col + 2; ! 761: off_rez = offset; ! 762: break; ! 763: } ! 764: } ! 765: if (!off_rez) ! 766: { ! 767: fclose(fp); ! 768: Log_Printf(LOG_ERROR, "'%s' not a valid INF file, #E resolution line missing -> autostarting / resolution overriding not possible!\n", infname); ! 769: return NULL; ! 770: } ! 771: /* write rest of INF file & seek back to start */ ! 772: if (!(fwrite(contents+offset, size-offset-1, 1, fp) && fseek(fp, 0, SEEK_SET) == 0)) ! 773: { ! 774: fclose(fp); ! 775: Log_Printf(LOG_ERROR, "Virtual '%s' file writing failed!\n", infname); ! 776: return NULL; ! 777: } ! 778: if (prgname) ! 779: Log_Printf(LOG_WARN, "Virtual '%s' autostart file created for '%s'.\n", infname, prgname); ! 780: else ! 781: Log_Printf(LOG_WARN, "Virtual '%s' TOS resolution override file created.\n", infname); ! 782: return fp; ! 783: } ! 784: ! 785: ! 786: /*-----------------------------------------------------------------------*/ ! 787: /** ! 788: * Create a temporary TOS INF file for autostarting and resolution overriding. ! 789: * ! 790: * File has TOS version specific differences, so it needs to be re-created ! 791: * on each boot in case user changed TOS version. ! 792: * ! 793: * Called at end of TOS ROM loading. ! 794: */ ! 795: void INF_CreateOverride(void) ! 796: { ! 797: char *contents; ! 798: const char *err, *val; ! 799: int size, res, res_col, opt_id; ! 800: ! 801: if ((opt_id = INF_ValidateResolution(&res, &val, &err))) ! 802: { ! 803: Opt_ShowError(opt_id, val, err); ! 804: bQuitProgram = true; ! 805: return; ! 806: } ! 807: ! 808: /* in case TOS didn't for some reason close it on previous boot */ ! 809: INF_CloseOverride(TosOverride.file); ! 810: ! 811: /* INF overriding needed? */ ! 812: if (!(TosOverride.prgname || TosOverride.reso)) ! 813: return; ! 814: ! 815: /* GEMDOS HD / INF overriding not supported? */ ! 816: if (TosVersion < 0x0104) ! 817: { ! 818: Log_Printf(LOG_WARN, "Only TOS versions >= 1.04 support autostarting & resolution overriding!\n"); ! 819: return; ! 820: } ! 821: ! 822: contents = get_inf_file(&TosOverride.infname, &size, &res_col); ! 823: if (contents) ! 824: { ! 825: TosOverride.file = write_inf_file(contents, size, res, res_col); ! 826: free(contents); ! 827: } ! 828: } ! 829: ! 830: ! 831: /*-----------------------------------------------------------------------*/ ! 832: /** ! 833: * Whether INF file overriding needs GEMDOS ! 834: * interception or Fopen() check enabling ! 835: */ ! 836: bool INF_Overriding(autostart_t t) ! 837: { ! 838: if (t == AUTOSTART_FOPEN) ! 839: return (bool)TosOverride.file; ! 840: return (((bool)TosOverride.prgname) || ((bool)TosOverride.reso)); ! 841: } ! 842: ! 843: /*-----------------------------------------------------------------------*/ ! 844: /** ! 845: * If given name matches virtual INF file name, return its handle, NULL otherwise ! 846: */ ! 847: FILE *INF_OpenOverride(const char *filename) ! 848: { ! 849: if (TosOverride.file && strcmp(filename, TosOverride.infname) == 0) ! 850: { ! 851: /* whether to "autostart" also exception debugging? */ ! 852: if (ConfigureParams.Debugger.nExceptionDebugMask & EXCEPT_AUTOSTART) ! 853: { ! 854: ExceptionDebugMask = ConfigureParams.Debugger.nExceptionDebugMask & ~EXCEPT_AUTOSTART; ! 855: fprintf(stderr, "Exception debugging enabled (0x%x).\n", ExceptionDebugMask); ! 856: } ! 857: Log_Printf(LOG_WARN, "Virtual INF file '%s' matched.\n", filename); ! 858: return TosOverride.file; ! 859: } ! 860: return NULL; ! 861: } ! 862: ! 863: /*-----------------------------------------------------------------------*/ ! 864: /** ! 865: * If given handle matches virtual INF file, close it and return true, ! 866: * false otherwise. ! 867: */ ! 868: bool INF_CloseOverride(FILE *fp) ! 869: { ! 870: if (fp && fp == TosOverride.file) ! 871: { ! 872: /* Remove virtual INF file after TOS has ! 873: * read it enough times to do autostarting etc. ! 874: * Otherwise user may try change desktop settings ! 875: * and save them, but they would be lost. ! 876: */ ! 877: fclose(TosOverride.file); ! 878: TosOverride.file = NULL; ! 879: Log_Printf(LOG_WARN, "Virtual INF file removed.\n"); ! 880: return true; ! 881: } ! 882: return false; ! 883: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.