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

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

unix.superglobalmegacorp.com

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