Search
lxdream.org :: lxdream/src/loader.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/loader.c
changeset 1298:d0eb2307b847
prev1296:30ecee61f811
next1300:d18488c8668b
author nkeynes
date Wed Feb 04 08:38:23 2015 +1000 (5 years ago)
permissions -rw-r--r--
last change Fix assorted compile warnings reported by Clang
file annotate diff log raw
nkeynes@26
     1
/**
nkeynes@561
     2
 * $Id$
nkeynes@1
     3
 *
nkeynes@26
     4
 * File loading routines, mostly for loading demos without going through the
nkeynes@1108
     5
 * whole procedure of manually making a CD image for them.
nkeynes@26
     6
 *
nkeynes@26
     7
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@26
     8
 *
nkeynes@26
     9
 * This program is free software; you can redistribute it and/or modify
nkeynes@26
    10
 * it under the terms of the GNU General Public License as published by
nkeynes@26
    11
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@26
    12
 * (at your option) any later version.
nkeynes@26
    13
 *
nkeynes@26
    14
 * This program is distributed in the hope that it will be useful,
nkeynes@26
    15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@26
    16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@26
    17
 * GNU General Public License for more details.
nkeynes@1
    18
 */
nkeynes@1
    19
nkeynes@481
    20
#include <unistd.h>
nkeynes@1
    21
#include <stdio.h>
nkeynes@736
    22
#include <string.h>
nkeynes@1
    23
#include <fcntl.h>
nkeynes@1
    24
#include <sys/stat.h>
nkeynes@1
    25
#include <errno.h>
nkeynes@1
    26
#include <stdint.h>
nkeynes@1296
    27
#include <glib.h>
nkeynes@105
    28
#include <elf.h>
nkeynes@26
    29
#include "mem.h"
nkeynes@26
    30
#include "bootstrap.h"
nkeynes@171
    31
#include "dreamcast.h"
nkeynes@450
    32
#include "config.h"
nkeynes@427
    33
#include "loader.h"
nkeynes@1108
    34
#include "drivers/cdrom/cdrom.h"
nkeynes@1108
    35
#include "drivers/cdrom/isofs.h"
nkeynes@1109
    36
#include "gdrom/gdrom.h"
nkeynes@481
    37
nkeynes@1108
    38
const char bootstrap_magic[32] = "SEGA SEGAKATANA SEGA ENTERPRISES";
nkeynes@1108
    39
const char iso_magic[6] = "\001CD001";
nkeynes@26
    40
char *file_loader_extensions[][2] = { 
nkeynes@736
    41
        { "sbi", "Self Boot Inducer" },
nkeynes@736
    42
        { "bin", "SH4 Bin file" },
nkeynes@736
    43
        { NULL, NULL } };
nkeynes@26
    44
nkeynes@1109
    45
static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err );
nkeynes@1109
    46
static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err );
nkeynes@1109
    47
static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err );
nkeynes@1109
    48
static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err );
nkeynes@1
    49
nkeynes@1108
    50
nkeynes@1109
    51
nkeynes@1109
    52
lxdream_file_type_t file_identify( const gchar *filename, int fd, ERROR *err )
nkeynes@1108
    53
{
nkeynes@1108
    54
    char buf[32];
nkeynes@1109
    55
    lxdream_file_type_t result = FILE_UNKNOWN;
nkeynes@1109
    56
    gboolean mustClose = FALSE;
nkeynes@1109
    57
    off_t posn;
nkeynes@1108
    58
nkeynes@1109
    59
    if( fd == -1 ) {
nkeynes@1109
    60
        fd = open( filename, O_RDONLY );
nkeynes@1109
    61
        if( fd == -1 ) {
nkeynes@1109
    62
            SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
nkeynes@1109
    63
            return FILE_ERROR;
nkeynes@1109
    64
        }
nkeynes@1109
    65
        mustClose = TRUE;
nkeynes@1109
    66
    } else {
nkeynes@1109
    67
        /* Save current file position */
nkeynes@1109
    68
        posn = lseek(fd, 0, SEEK_CUR);
nkeynes@1109
    69
        if( posn == -1 ) {
nkeynes@1109
    70
            SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
nkeynes@1109
    71
            return FILE_ERROR;
nkeynes@1109
    72
        }
nkeynes@1108
    73
    }
nkeynes@1108
    74
nkeynes@1109
    75
    int status = read(fd, buf, 32);
nkeynes@1109
    76
    if( status == -1 ) {
nkeynes@1109
    77
        SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
nkeynes@1109
    78
        result = FILE_ERROR;
nkeynes@1109
    79
    } else if( status != 32 ) {
nkeynes@1109
    80
        result = FILE_UNKNOWN;
nkeynes@1109
    81
    } else if( buf[0] == 0x7F && buf[1] == 'E' &&
nkeynes@1108
    82
            buf[2] == 'L' && buf[3] == 'F' ) {
nkeynes@1109
    83
        result = FILE_ELF;
nkeynes@1108
    84
    } else if( memcmp( buf, "PK\x03\x04", 4 ) == 0 ) {
nkeynes@1109
    85
        result = FILE_ZIP;
nkeynes@1108
    86
    } else if( memcmp( buf, DREAMCAST_SAVE_MAGIC, 16 ) == 0 ) {
nkeynes@1109
    87
        result = FILE_SAVE_STATE;
nkeynes@1108
    88
    } else if( lseek( fd, 32768, SEEK_SET ) == 32768 &&
nkeynes@1108
    89
            read( fd, buf, 8 ) == 8 &&
nkeynes@1108
    90
            memcmp( buf, iso_magic, 6) == 0 ) {
nkeynes@1109
    91
        result = FILE_ISO;
nkeynes@1109
    92
    } else {
nkeynes@1109
    93
        /* Check the file extension - .bin = sh4 binary */
nkeynes@1109
    94
        int len = strlen(filename);
nkeynes@1109
    95
        struct stat st;
nkeynes@1109
    96
nkeynes@1109
    97
        if( len > 4 && strcasecmp(filename + (len-4), ".bin") == 0 &&
nkeynes@1109
    98
            fstat(fd, &st) != -1 && st.st_size <= BINARY_MAX_SIZE ) {
nkeynes@1109
    99
            result = FILE_BINARY;
nkeynes@1109
   100
        }
nkeynes@1108
   101
    }
nkeynes@1109
   102
nkeynes@1109
   103
    if( mustClose ) {
nkeynes@1109
   104
        close(fd);
nkeynes@1109
   105
    } else {
nkeynes@1109
   106
        lseek( fd, posn, SEEK_SET );
nkeynes@1109
   107
    }
nkeynes@1109
   108
    return result;
nkeynes@1108
   109
}
nkeynes@427
   110
nkeynes@427
   111
nkeynes@1109
   112
gboolean file_load_exec( const gchar *filename, ERROR *err )
nkeynes@1
   113
{
nkeynes@446
   114
    gboolean result = TRUE;
nkeynes@736
   115
nkeynes@1
   116
    int fd = open( filename, O_RDONLY );
nkeynes@1
   117
    if( fd == -1 ) {
nkeynes@1109
   118
        SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
nkeynes@26
   119
        return FALSE;
nkeynes@1
   120
    }
nkeynes@736
   121
nkeynes@1109
   122
    lxdream_file_type_t type = file_identify(filename, fd, err);
nkeynes@1109
   123
    switch( type ) {
nkeynes@1109
   124
    case FILE_ERROR:
nkeynes@1109
   125
        result = FALSE;
nkeynes@1109
   126
        break;
nkeynes@1109
   127
    case FILE_ELF:
nkeynes@1109
   128
        result = file_load_elf( filename, fd, err );
nkeynes@1109
   129
        break;
nkeynes@1109
   130
    case FILE_BINARY:
nkeynes@1109
   131
        result = file_load_binary( filename, fd, err );
nkeynes@1109
   132
        break;
nkeynes@1109
   133
    default:
nkeynes@1109
   134
        SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized as an executable binary", filename );
nkeynes@1109
   135
        result = FALSE;
nkeynes@1109
   136
        break;
nkeynes@1109
   137
    }
nkeynes@736
   138
nkeynes@1109
   139
    close(fd);
nkeynes@1109
   140
    return result;
nkeynes@1109
   141
}
nkeynes@1109
   142
nkeynes@1109
   143
lxdream_file_type_t file_load_magic( const gchar *filename, gboolean wrap_exec, ERROR *err )
nkeynes@1109
   144
{
nkeynes@1109
   145
    gboolean result;
nkeynes@1109
   146
    /* Try disc types first */
nkeynes@1109
   147
    cdrom_disc_t disc = cdrom_disc_open( filename, err );
nkeynes@1109
   148
    if( disc != NULL ) {
nkeynes@1109
   149
        gdrom_mount_disc(disc);
nkeynes@1109
   150
        return FILE_DISC;
nkeynes@1298
   151
    } else if( !IS_ERROR_CODE(err,LX_ERR_FILE_UNKNOWN) ) {
nkeynes@1109
   152
        return FILE_ERROR;
nkeynes@1
   153
    }
nkeynes@1109
   154
nkeynes@1109
   155
    int fd = open( filename, O_RDONLY );
nkeynes@1109
   156
    if( fd == -1 ) {
nkeynes@1109
   157
        SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
nkeynes@1109
   158
        return FILE_ERROR;
nkeynes@1109
   159
    }
nkeynes@1109
   160
nkeynes@1109
   161
    lxdream_file_type_t type = file_identify(filename, fd, err);
nkeynes@1109
   162
    switch( type ) {
nkeynes@1109
   163
    case FILE_ERROR:
nkeynes@1109
   164
        result = FALSE;
nkeynes@1109
   165
        break;
nkeynes@1109
   166
    case FILE_ELF:
nkeynes@1109
   167
        if( wrap_exec ) {
nkeynes@1109
   168
            disc = cdrom_wrap_elf( CDROM_DISC_XA, filename, fd, err );
nkeynes@1109
   169
            result = disc != NULL;
nkeynes@1109
   170
            if( disc != NULL ) {
nkeynes@1109
   171
                gdrom_mount_disc(disc);
nkeynes@1109
   172
            }
nkeynes@1
   173
        } else {
nkeynes@1109
   174
            result = file_load_elf( filename, fd, err );
nkeynes@1109
   175
        }
nkeynes@1109
   176
        break;
nkeynes@1109
   177
    case FILE_BINARY:
nkeynes@1109
   178
        if( wrap_exec ) {
nkeynes@1109
   179
            disc = cdrom_wrap_binary( CDROM_DISC_XA, filename, fd, err );
nkeynes@1109
   180
            result = disc != NULL;
nkeynes@1109
   181
            if( disc != NULL ) {
nkeynes@1109
   182
                gdrom_mount_disc(disc);
nkeynes@1
   183
            }
nkeynes@1109
   184
        } else {
nkeynes@1109
   185
            result = file_load_binary( filename, fd, err );
nkeynes@1
   186
        }
nkeynes@1109
   187
        break;
nkeynes@1109
   188
    case FILE_SAVE_STATE:
nkeynes@1109
   189
        result = dreamcast_load_state( filename );
nkeynes@1109
   190
        break;
nkeynes@1109
   191
    case FILE_ZIP:
nkeynes@1109
   192
        SET_ERROR( err, LX_ERR_FILE_UNSUP, "ZIP/SBI not currently supported" );
nkeynes@736
   193
        result = FALSE;
nkeynes@1109
   194
        break;
nkeynes@1109
   195
    case FILE_ISO:
nkeynes@1109
   196
        SET_ERROR( err, LX_ERR_FILE_UNSUP, "ISO files are not currently supported" );
nkeynes@736
   197
        result = FALSE;
nkeynes@1109
   198
        break;
nkeynes@1109
   199
    default:
nkeynes@1109
   200
        SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized", filename );
nkeynes@1109
   201
        result = FALSE;
nkeynes@1109
   202
        break;
nkeynes@446
   203
    }
nkeynes@1
   204
    close(fd);
nkeynes@1109
   205
    if( result ) {
nkeynes@1109
   206
        CLEAR_ERROR(err);
nkeynes@1109
   207
        return type;
nkeynes@1109
   208
    }
nkeynes@1109
   209
    return FILE_ERROR;
nkeynes@1
   210
}
nkeynes@19
   211
nkeynes@543
   212
void file_load_postload( const gchar *filename, int pc )
nkeynes@110
   213
{
nkeynes@1036
   214
    gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
nkeynes@825
   215
    if( bootstrap_file != NULL && bootstrap_file[0] != '\0' ) {
nkeynes@736
   216
        /* Load in a bootstrap before the binary, to initialize everything
nkeynes@736
   217
         * correctly
nkeynes@736
   218
         */
nkeynes@543
   219
        if( mem_load_block( bootstrap_file, BOOTSTRAP_LOAD_ADDR, BOOTSTRAP_SIZE ) == 0 ) {
nkeynes@1100
   220
            dreamcast_program_loaded( filename, BOOTSTRAP_ENTRY_ADDR );
nkeynes@1036
   221
            g_free(bootstrap_file);
nkeynes@736
   222
            return;
nkeynes@736
   223
        }
nkeynes@88
   224
    }
nkeynes@543
   225
    dreamcast_program_loaded( filename, pc );
nkeynes@1036
   226
    g_free(bootstrap_file);
nkeynes@110
   227
}    
nkeynes@110
   228
nkeynes@1298
   229
static gboolean is_arch( Elf32_Ehdr *head, Elf32_Half mach ) {
nkeynes@1108
   230
    return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
nkeynes@1108
   231
            head->e_ident[EI_DATA] == ELFDATA2LSB &&
nkeynes@1108
   232
            head->e_ident[EI_VERSION] == 1 &&
nkeynes@1108
   233
            head->e_type == ET_EXEC &&
nkeynes@1298
   234
            head->e_machine == mach &&
nkeynes@1108
   235
            head->e_version == 1 );
nkeynes@1108
   236
}
nkeynes@1108
   237
nkeynes@1298
   238
#define is_sh4_elf(head) is_arch(head, EM_SH)
nkeynes@1298
   239
#define is_arm_elf(head) is_arch(head, EM_ARM)
nkeynes@1108
   240
nkeynes@1109
   241
static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err )
nkeynes@105
   242
{
nkeynes@105
   243
    Elf32_Ehdr head;
nkeynes@105
   244
    Elf32_Phdr phdr;
nkeynes@105
   245
    int i;
nkeynes@105
   246
nkeynes@105
   247
    if( read( fd, &head, sizeof(head) ) != sizeof(head) )
nkeynes@736
   248
        return FALSE;
nkeynes@1108
   249
    if( !is_sh4_elf(&head) ) {
nkeynes@1109
   250
        SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
nkeynes@736
   251
        return FALSE;
nkeynes@105
   252
    }
nkeynes@105
   253
nkeynes@105
   254
    /* Program headers */
nkeynes@105
   255
    for( i=0; i<head.e_phnum; i++ ) {
nkeynes@736
   256
        lseek( fd, head.e_phoff + i*head.e_phentsize, SEEK_SET );
nkeynes@736
   257
        read( fd, &phdr, sizeof(phdr) );
nkeynes@736
   258
        if( phdr.p_type == PT_LOAD ) {
nkeynes@736
   259
            lseek( fd, phdr.p_offset, SEEK_SET );
nkeynes@736
   260
            sh4ptr_t target = mem_get_region( phdr.p_vaddr );
nkeynes@736
   261
            read( fd, target, phdr.p_filesz );
nkeynes@736
   262
            if( phdr.p_memsz > phdr.p_filesz ) {
nkeynes@736
   263
                memset( target + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz );
nkeynes@736
   264
            }
nkeynes@736
   265
        }
nkeynes@105
   266
    }
nkeynes@736
   267
nkeynes@543
   268
    file_load_postload( filename, head.e_entry );
nkeynes@446
   269
    return TRUE;
nkeynes@105
   270
}
nkeynes@1108
   271
nkeynes@1109
   272
static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err )
nkeynes@1109
   273
{
nkeynes@1109
   274
    struct stat st;
nkeynes@1109
   275
nkeynes@1109
   276
    if( fstat( fd, &st ) == -1 ) {
nkeynes@1109
   277
        SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
nkeynes@1109
   278
        return FALSE;
nkeynes@1109
   279
    }
nkeynes@1109
   280
nkeynes@1109
   281
    if( st.st_size > BINARY_MAX_SIZE ) {
nkeynes@1109
   282
        SET_ERROR( err, LX_ERR_FILE_INVALID, "Binary file '%s' is too large to fit in memory", filename );
nkeynes@1109
   283
        return FALSE;
nkeynes@1109
   284
    }
nkeynes@1109
   285
nkeynes@1109
   286
    sh4ptr_t target = mem_get_region( BINARY_LOAD_ADDR );
nkeynes@1109
   287
    if( read( fd, target, st.st_size ) != st.st_size ) {
nkeynes@1109
   288
        SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
nkeynes@1109
   289
        return FALSE;
nkeynes@1109
   290
    }
nkeynes@1109
   291
nkeynes@1109
   292
    file_load_postload( filename, BINARY_LOAD_ADDR );
nkeynes@1109
   293
    return TRUE;
nkeynes@1109
   294
}
nkeynes@1109
   295
nkeynes@1108
   296
/**
nkeynes@1108
   297
 * Create a new CDROM disc containing a single 1ST_READ.BIN.
nkeynes@1108
   298
 * @param type The disc type - must be CDROM_DISC_GDROM or CDROM_DISC_XA
nkeynes@1108
   299
 * @param bin The binary data (takes ownership)
nkeynes@1108
   300
 * @param bin_size
nkeynes@1108
   301
 */
nkeynes@1108
   302
cdrom_disc_t cdrom_disc_new_wrapped_binary( cdrom_disc_type_t type, const gchar *filename, unsigned char *bin, size_t bin_size,
nkeynes@1108
   303
                                            ERROR *err )
nkeynes@1108
   304
{
nkeynes@1108
   305
    IsoImage *iso = NULL;
nkeynes@1108
   306
    unsigned char *data = bin;
nkeynes@1108
   307
    cdrom_lba_t start_lba = 45000; /* GDROM_START */
nkeynes@1108
   308
    char bootstrap[32768];
nkeynes@1108
   309
nkeynes@1109
   310
    /* 1. Load in the bootstrap: Note failures here are considered configuration errors */
nkeynes@1108
   311
    gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
nkeynes@1108
   312
    if( bootstrap_file == NULL || bootstrap_file[0] == '\0' ) {
nkeynes@1108
   313
        g_free(data);
nkeynes@1109
   314
        SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file is not configured" );
nkeynes@1108
   315
        return NULL;
nkeynes@1108
   316
    }
nkeynes@1108
   317
nkeynes@1108
   318
    FILE *f = fopen( bootstrap_file, "ro" );
nkeynes@1108
   319
    if( f == NULL ) {
nkeynes@1108
   320
        g_free(data);
nkeynes@1109
   321
        SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' could not be opened", bootstrap_file );
nkeynes@1108
   322
        return FALSE;
nkeynes@1108
   323
    }
nkeynes@1108
   324
    size_t len = fread( bootstrap, 1, 32768, f );
nkeynes@1108
   325
    fclose(f);
nkeynes@1108
   326
    if( len != 32768 ) {
nkeynes@1108
   327
        g_free(data);
nkeynes@1109
   328
        SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' is invalid", bootstrap_file );
nkeynes@1108
   329
        return FALSE;
nkeynes@1108
   330
    }
nkeynes@1108
   331
nkeynes@1108
   332
    /* 2. Scramble the binary if necessary (and set type settings) */
nkeynes@1108
   333
    if( type != CDROM_DISC_GDROM ) {
nkeynes@1108
   334
        /* scramble the binary if we're going the MIL-CD route */
nkeynes@1108
   335
        unsigned char *scramblebin = g_malloc(bin_size);
nkeynes@1108
   336
        bootprogram_scramble( scramblebin, bin, bin_size );
nkeynes@1108
   337
        data = scramblebin;
nkeynes@1108
   338
        start_lba = 0x2DB6; /* CDROM_START (does it matter?) */
nkeynes@1108
   339
        g_free(bin);
nkeynes@1108
   340
    }
nkeynes@1108
   341
nkeynes@1108
   342
    /* 3. Frob the bootstrap data */
nkeynes@1108
   343
    dc_bootstrap_head_t boot_header = (dc_bootstrap_head_t)bootstrap;
nkeynes@1108
   344
    memcpy( boot_header->boot_file, "1ST_READ.BIN    ", 16 );
nkeynes@1108
   345
    char tmp[129];
nkeynes@1108
   346
    int name_len = snprintf( tmp, 129, "lxdream wrapped image: %s", filename );
nkeynes@1108
   347
    if( name_len < 128 )
nkeynes@1108
   348
        memset( tmp+name_len, ' ', 128-name_len );
nkeynes@1108
   349
    memcpy( boot_header->product_name, tmp, 128 );
nkeynes@1108
   350
//    bootstrap_update_crc(bootstrap);
nkeynes@1108
   351
nkeynes@1108
   352
nkeynes@1108
   353
    /* 4. Build the ISO image */
nkeynes@1108
   354
    int status = iso_image_new("autocd", &iso);
nkeynes@1108
   355
    if( status != 1 ) {
nkeynes@1108
   356
        g_free(data);
nkeynes@1109
   357
        SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
nkeynes@1108
   358
        return NULL;
nkeynes@1108
   359
    }
nkeynes@1108
   360
nkeynes@1108
   361
    IsoStream *stream;
nkeynes@1152
   362
    if( iso_mem_stream_new(data, bin_size, &stream) != 1 ) {
nkeynes@1108
   363
        g_free(data);
nkeynes@1108
   364
        iso_image_unref(iso);
nkeynes@1109
   365
        SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
nkeynes@1108
   366
        return NULL;
nkeynes@1108
   367
    }
nkeynes@1108
   368
    iso_tree_add_new_file(iso_image_get_root(iso), "1ST_READ.BIN", stream, NULL);
nkeynes@1108
   369
    sector_source_t track = iso_sector_source_new( iso, SECTOR_MODE2_FORM1, start_lba,
nkeynes@1108
   370
            bootstrap, err );
nkeynes@1108
   371
    if( track == NULL ) {
nkeynes@1108
   372
        iso_image_unref(iso);
nkeynes@1108
   373
        return NULL;
nkeynes@1108
   374
    }
nkeynes@1108
   375
nkeynes@1109
   376
    cdrom_disc_t disc = cdrom_disc_new_from_track( type, track, start_lba, err );
nkeynes@1108
   377
    iso_image_unref(iso);
nkeynes@1108
   378
    if( disc != NULL ) {
nkeynes@1108
   379
        disc->name = g_strdup(filename);
nkeynes@1109
   380
    } 
nkeynes@1108
   381
    return disc;
nkeynes@1108
   382
}
nkeynes@1108
   383
nkeynes@1109
   384
static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
nkeynes@1108
   385
{
nkeynes@1108
   386
    Elf32_Ehdr head;
nkeynes@1108
   387
    int i;
nkeynes@1108
   388
nkeynes@1108
   389
    /* Check the file header is actually an SH4 binary */
nkeynes@1108
   390
    if( read( fd, &head, sizeof(head) ) != sizeof(head) )
nkeynes@1108
   391
        return FALSE;
nkeynes@1109
   392
nkeynes@1108
   393
    if( !is_sh4_elf(&head) ) {
nkeynes@1109
   394
        SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
nkeynes@1108
   395
        return FALSE;
nkeynes@1108
   396
    }
nkeynes@1108
   397
    if( head.e_entry != BINARY_LOAD_ADDR ) {
nkeynes@1109
   398
        SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 Binary has incorrect entry point (should be %08X but is %08X)", BINARY_LOAD_ADDR, head.e_entry );
nkeynes@1108
   399
        return FALSE;
nkeynes@1108
   400
    }
nkeynes@1108
   401
nkeynes@1108
   402
    /* Load the program headers */
nkeynes@1108
   403
    Elf32_Phdr phdr[head.e_phnum];
nkeynes@1108
   404
    lseek( fd, head.e_phoff, SEEK_SET );
nkeynes@1108
   405
    if( read( fd, phdr, sizeof(phdr) ) != sizeof(phdr) ) {
nkeynes@1109
   406
        SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not a valid executable file" );
nkeynes@1108
   407
        return FALSE;
nkeynes@1108
   408
    }
nkeynes@1108
   409
nkeynes@1108
   410
    sh4addr_t start = (sh4addr_t)-1, end=0;
nkeynes@1108
   411
    /* Scan program headers for memory range in use */
nkeynes@1108
   412
    for( i=0; i<head.e_phnum; i++ ) {
nkeynes@1108
   413
        if( phdr[i].p_type == PT_LOAD ) {
nkeynes@1108
   414
            if( phdr[i].p_vaddr < start )
nkeynes@1108
   415
                start = phdr[i].p_vaddr;
nkeynes@1108
   416
            if( phdr[i].p_vaddr + phdr[i].p_memsz > end )
nkeynes@1108
   417
                end = phdr[i].p_vaddr + phdr[i].p_memsz;
nkeynes@1108
   418
        }
nkeynes@1108
   419
    }
nkeynes@1108
   420
nkeynes@1108
   421
    if( start != BINARY_LOAD_ADDR ) {
nkeynes@1109
   422
        SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 Binary has incorrect load address (should be %08X but is %08X)", BINARY_LOAD_ADDR, start );
nkeynes@1108
   423
        return FALSE;
nkeynes@1108
   424
    }
nkeynes@1108
   425
    if( end >= 0x8D000000 ) {
nkeynes@1109
   426
        SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 binary is too large to fit in memory (end address is %08X)", end );
nkeynes@1108
   427
        return FALSE;
nkeynes@1108
   428
    }
nkeynes@1108
   429
nkeynes@1108
   430
    /* Load the program into memory */
nkeynes@1298
   431
    unsigned char *program = g_malloc0( end-start );
nkeynes@1108
   432
    for( i=0; i<head.e_phnum; i++ ) {
nkeynes@1108
   433
        if( phdr[i].p_type == PT_LOAD ) {
nkeynes@1108
   434
            lseek( fd, phdr[i].p_offset, SEEK_SET );
nkeynes@1108
   435
            uint32_t size = MIN( phdr[i].p_filesz, phdr[i].p_memsz);
nkeynes@1117
   436
            int status = read( fd, program + phdr[i].p_vaddr - start, size );
nkeynes@1109
   437
            if( status == -1 ) {
nkeynes@1109
   438
                SET_ERROR( err, LX_ERR_FILE_IOERROR, "I/O error reading SH4 binary %s (%s)", filename, strerror(errno) );
nkeynes@1109
   439
            } else if( status != size ) {
nkeynes@1109
   440
                SET_ERROR( err, LX_ERR_FILE_IOERROR, "SH4 binary %s is corrupt", filename );
nkeynes@1109
   441
            }            
nkeynes@1108
   442
        }
nkeynes@1108
   443
    }
nkeynes@1108
   444
nkeynes@1108
   445
    /* And finally pass it over to the disc wrapper */
nkeynes@1108
   446
    return cdrom_disc_new_wrapped_binary(type, filename, program, end-start, err );
nkeynes@1108
   447
}
nkeynes@1108
   448
nkeynes@1109
   449
static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
nkeynes@1108
   450
{
nkeynes@1109
   451
    struct stat st;
nkeynes@1298
   452
    unsigned char *data;
nkeynes@1109
   453
    size_t len;
nkeynes@1109
   454
nkeynes@1109
   455
    if( fstat(fd, &st) == -1 ) {
nkeynes@1109
   456
        SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
nkeynes@1108
   457
        return NULL;
nkeynes@1108
   458
    }
nkeynes@1108
   459
nkeynes@1109
   460
    data = g_malloc(st.st_size);
nkeynes@1109
   461
    len = read( fd, data, st.st_size );
nkeynes@1109
   462
    if( len != st.st_size ) {
nkeynes@1109
   463
        SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
nkeynes@1109
   464
        free(data);
nkeynes@1108
   465
        return NULL;
nkeynes@1108
   466
    }
nkeynes@1108
   467
nkeynes@1109
   468
    return cdrom_disc_new_wrapped_binary( type, filename, data, st.st_size, err );
nkeynes@1108
   469
}
nkeynes@1109
   470
nkeynes@1109
   471
cdrom_disc_t cdrom_wrap_magic( cdrom_disc_type_t type, const gchar *filename, ERROR *err )
nkeynes@1109
   472
{
nkeynes@1109
   473
    cdrom_disc_t disc = NULL;
nkeynes@1109
   474
nkeynes@1109
   475
    int fd = open( filename, O_RDONLY );
nkeynes@1109
   476
    if( fd == -1 ) {
nkeynes@1109
   477
        SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s'", filename );
nkeynes@1109
   478
        return NULL;
nkeynes@1109
   479
    }
nkeynes@1109
   480
nkeynes@1109
   481
    lxdream_file_type_t filetype = file_identify( filename, fd, err );
nkeynes@1109
   482
    switch( filetype ) {
nkeynes@1109
   483
    case FILE_ELF:
nkeynes@1109
   484
        disc = cdrom_wrap_elf(type, filename, fd, err);
nkeynes@1109
   485
        break;
nkeynes@1109
   486
    case FILE_BINARY:
nkeynes@1109
   487
        disc = cdrom_wrap_binary(type, filename, fd, err);
nkeynes@1109
   488
        break;
nkeynes@1109
   489
    default:
nkeynes@1109
   490
        SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' cannot be wrapped (not a recognized binary)", filename );
nkeynes@1109
   491
        break;
nkeynes@1109
   492
    }
nkeynes@1109
   493
nkeynes@1109
   494
    close(fd);
nkeynes@1109
   495
    return disc;
nkeynes@1109
   496
nkeynes@1109
   497
}
.