Annotation of hatari/src/inffile.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.