Search
lxdream.org :: lxdream/src/loader.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/loader.c
changeset 1152:6464d890cc9e
prev1117:0b14a8ec373b
next1296:30ecee61f811
author nkeynes
date Tue Feb 28 18:22:52 2012 +1000 (12 years ago)
permissions -rw-r--r--
last change Add a GL-only video driver for android usage (since the Java code is
responsible for creating the context)
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@1108
    27
#include <glib/gstrfuncs.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@1109
   151
    } else if( 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@110
   229
nkeynes@1109
   230
static gboolean is_sh4_elf( Elf32_Ehdr *head )
nkeynes@1108
   231
{
nkeynes@1108
   232
    return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
nkeynes@1108
   233
            head->e_ident[EI_DATA] == ELFDATA2LSB &&
nkeynes@1108
   234
            head->e_ident[EI_VERSION] == 1 &&
nkeynes@1108
   235
            head->e_type == ET_EXEC &&
nkeynes@1108
   236
            head->e_machine == EM_SH &&
nkeynes@1108
   237
            head->e_version == 1 );
nkeynes@1108
   238
}
nkeynes@1108
   239
nkeynes@1109
   240
static gboolean is_arm_elf( Elf32_Ehdr *head )
nkeynes@1108
   241
{
nkeynes@1108
   242
    return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
nkeynes@1108
   243
            head->e_ident[EI_DATA] == ELFDATA2LSB &&
nkeynes@1108
   244
            head->e_ident[EI_VERSION] == 1 &&
nkeynes@1108
   245
            head->e_type == ET_EXEC &&
nkeynes@1108
   246
            head->e_machine == EM_ARM &&
nkeynes@1108
   247
            head->e_version == 1 );
nkeynes@1108
   248
}
nkeynes@1108
   249
nkeynes@1109
   250
static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err )
nkeynes@105
   251
{
nkeynes@105
   252
    Elf32_Ehdr head;
nkeynes@105
   253
    Elf32_Phdr phdr;
nkeynes@105
   254
    int i;
nkeynes@105
   255
nkeynes@105
   256
    if( read( fd, &head, sizeof(head) ) != sizeof(head) )
nkeynes@736
   257
        return FALSE;
nkeynes@1108
   258
    if( !is_sh4_elf(&head) ) {
nkeynes@1109
   259
        SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
nkeynes@736
   260
        return FALSE;
nkeynes@105
   261
    }
nkeynes@105
   262
nkeynes@105
   263
    /* Program headers */
nkeynes@105
   264
    for( i=0; i<head.e_phnum; i++ ) {
nkeynes@736
   265
        lseek( fd, head.e_phoff + i*head.e_phentsize, SEEK_SET );
nkeynes@736
   266
        read( fd, &phdr, sizeof(phdr) );
nkeynes@736
   267
        if( phdr.p_type == PT_LOAD ) {
nkeynes@736
   268
            lseek( fd, phdr.p_offset, SEEK_SET );
nkeynes@736
   269
            sh4ptr_t target = mem_get_region( phdr.p_vaddr );
nkeynes@736
   270
            read( fd, target, phdr.p_filesz );
nkeynes@736
   271
            if( phdr.p_memsz > phdr.p_filesz ) {
nkeynes@736
   272
                memset( target + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz );
nkeynes@736
   273
            }
nkeynes@736
   274
        }
nkeynes@105
   275
    }
nkeynes@736
   276
nkeynes@543
   277
    file_load_postload( filename, head.e_entry );
nkeynes@446
   278
    return TRUE;
nkeynes@105
   279
}
nkeynes@1108
   280
nkeynes@1109
   281
static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err )
nkeynes@1109
   282
{
nkeynes@1109
   283
    struct stat st;
nkeynes@1109
   284
nkeynes@1109
   285
    if( fstat( fd, &st ) == -1 ) {
nkeynes@1109
   286
        SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
nkeynes@1109
   287
        return FALSE;
nkeynes@1109
   288
    }
nkeynes@1109
   289
nkeynes@1109
   290
    if( st.st_size > BINARY_MAX_SIZE ) {
nkeynes@1109
   291
        SET_ERROR( err, LX_ERR_FILE_INVALID, "Binary file '%s' is too large to fit in memory", filename );
nkeynes@1109
   292
        return FALSE;
nkeynes@1109
   293
    }
nkeynes@1109
   294
nkeynes@1109
   295
    sh4ptr_t target = mem_get_region( BINARY_LOAD_ADDR );
nkeynes@1109
   296
    if( read( fd, target, st.st_size ) != st.st_size ) {
nkeynes@1109
   297
        SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
nkeynes@1109
   298
        return FALSE;
nkeynes@1109
   299
    }
nkeynes@1109
   300
nkeynes@1109
   301
    file_load_postload( filename, BINARY_LOAD_ADDR );
nkeynes@1109
   302
    return TRUE;
nkeynes@1109
   303
}
nkeynes@1109
   304
nkeynes@1108
   305
/**
nkeynes@1108
   306
 * Create a new CDROM disc containing a single 1ST_READ.BIN.
nkeynes@1108
   307
 * @param type The disc type - must be CDROM_DISC_GDROM or CDROM_DISC_XA
nkeynes@1108
   308
 * @param bin The binary data (takes ownership)
nkeynes@1108
   309
 * @param bin_size
nkeynes@1108
   310
 */
nkeynes@1108
   311
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
   312
                                            ERROR *err )
nkeynes@1108
   313
{
nkeynes@1108
   314
    IsoImage *iso = NULL;
nkeynes@1108
   315
    unsigned char *data = bin;
nkeynes@1108
   316
    cdrom_lba_t start_lba = 45000; /* GDROM_START */
nkeynes@1108
   317
    char bootstrap[32768];
nkeynes@1108
   318
nkeynes@1109
   319
    /* 1. Load in the bootstrap: Note failures here are considered configuration errors */
nkeynes@1108
   320
    gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
nkeynes@1108
   321
    if( bootstrap_file == NULL || bootstrap_file[0] == '\0' ) {
nkeynes@1108
   322
        g_free(data);
nkeynes@1109
   323
        SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file is not configured" );
nkeynes@1108
   324
        return NULL;
nkeynes@1108
   325
    }
nkeynes@1108
   326
nkeynes@1108
   327
    FILE *f = fopen( bootstrap_file, "ro" );
nkeynes@1108
   328
    if( f == NULL ) {
nkeynes@1108
   329
        g_free(data);
nkeynes@1109
   330
        SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' could not be opened", bootstrap_file );
nkeynes@1108
   331
        return FALSE;
nkeynes@1108
   332
    }
nkeynes@1108
   333
    size_t len = fread( bootstrap, 1, 32768, f );
nkeynes@1108
   334
    fclose(f);
nkeynes@1108
   335
    if( len != 32768 ) {
nkeynes@1108
   336
        g_free(data);
nkeynes@1109
   337
        SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' is invalid", bootstrap_file );
nkeynes@1108
   338
        return FALSE;
nkeynes@1108
   339
    }
nkeynes@1108
   340
nkeynes@1108
   341
    /* 2. Scramble the binary if necessary (and set type settings) */
nkeynes@1108
   342
    if( type != CDROM_DISC_GDROM ) {
nkeynes@1108
   343
        /* scramble the binary if we're going the MIL-CD route */
nkeynes@1108
   344
        unsigned char *scramblebin = g_malloc(bin_size);
nkeynes@1108
   345
        bootprogram_scramble( scramblebin, bin, bin_size );
nkeynes@1108
   346
        data = scramblebin;
nkeynes@1108
   347
        start_lba = 0x2DB6; /* CDROM_START (does it matter?) */
nkeynes@1108
   348
        g_free(bin);
nkeynes@1108
   349
    }
nkeynes@1108
   350
nkeynes@1108
   351
    /* 3. Frob the bootstrap data */
nkeynes@1108
   352
    dc_bootstrap_head_t boot_header = (dc_bootstrap_head_t)bootstrap;
nkeynes@1108
   353
    memcpy( boot_header->boot_file, "1ST_READ.BIN    ", 16 );
nkeynes@1108
   354
    char tmp[129];
nkeynes@1108
   355
    int name_len = snprintf( tmp, 129, "lxdream wrapped image: %s", filename );
nkeynes@1108
   356
    if( name_len < 128 )
nkeynes@1108
   357
        memset( tmp+name_len, ' ', 128-name_len );
nkeynes@1108
   358
    memcpy( boot_header->product_name, tmp, 128 );
nkeynes@1108
   359
//    bootstrap_update_crc(bootstrap);
nkeynes@1108
   360
nkeynes@1108
   361
nkeynes@1108
   362
    /* 4. Build the ISO image */
nkeynes@1108
   363
    int status = iso_image_new("autocd", &iso);
nkeynes@1108
   364
    if( status != 1 ) {
nkeynes@1108
   365
        g_free(data);
nkeynes@1109
   366
        SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
nkeynes@1108
   367
        return NULL;
nkeynes@1108
   368
    }
nkeynes@1108
   369
nkeynes@1108
   370
    IsoStream *stream;
nkeynes@1152
   371
    if( iso_mem_stream_new(data, bin_size, &stream) != 1 ) {
nkeynes@1108
   372
        g_free(data);
nkeynes@1108
   373
        iso_image_unref(iso);
nkeynes@1109
   374
        SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
nkeynes@1108
   375
        return NULL;
nkeynes@1108
   376
    }
nkeynes@1108
   377
    iso_tree_add_new_file(iso_image_get_root(iso), "1ST_READ.BIN", stream, NULL);
nkeynes@1108
   378
    sector_source_t track = iso_sector_source_new( iso, SECTOR_MODE2_FORM1, start_lba,
nkeynes@1108
   379
            bootstrap, err );
nkeynes@1108
   380
    if( track == NULL ) {
nkeynes@1108
   381
        iso_image_unref(iso);
nkeynes@1108
   382
        return NULL;
nkeynes@1108
   383
    }
nkeynes@1108
   384
nkeynes@1109
   385
    cdrom_disc_t disc = cdrom_disc_new_from_track( type, track, start_lba, err );
nkeynes@1108
   386
    iso_image_unref(iso);
nkeynes@1108
   387
    if( disc != NULL ) {
nkeynes@1108
   388
        disc->name = g_strdup(filename);
nkeynes@1109
   389
    } 
nkeynes@1108
   390
    return disc;
nkeynes@1108
   391
}
nkeynes@1108
   392
nkeynes@1109
   393
static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
nkeynes@1108
   394
{
nkeynes@1108
   395
    Elf32_Ehdr head;
nkeynes@1108
   396
    int i;
nkeynes@1108
   397
nkeynes@1108
   398
    /* Check the file header is actually an SH4 binary */
nkeynes@1108
   399
    if( read( fd, &head, sizeof(head) ) != sizeof(head) )
nkeynes@1108
   400
        return FALSE;
nkeynes@1109
   401
nkeynes@1108
   402
    if( !is_sh4_elf(&head) ) {
nkeynes@1109
   403
        SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
nkeynes@1108
   404
        return FALSE;
nkeynes@1108
   405
    }
nkeynes@1108
   406
    if( head.e_entry != BINARY_LOAD_ADDR ) {
nkeynes@1109
   407
        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
   408
        return FALSE;
nkeynes@1108
   409
    }
nkeynes@1108
   410
nkeynes@1108
   411
    /* Load the program headers */
nkeynes@1108
   412
    Elf32_Phdr phdr[head.e_phnum];
nkeynes@1108
   413
    lseek( fd, head.e_phoff, SEEK_SET );
nkeynes@1108
   414
    if( read( fd, phdr, sizeof(phdr) ) != sizeof(phdr) ) {
nkeynes@1109
   415
        SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not a valid executable file" );
nkeynes@1108
   416
        return FALSE;
nkeynes@1108
   417
    }
nkeynes@1108
   418
nkeynes@1108
   419
    sh4addr_t start = (sh4addr_t)-1, end=0;
nkeynes@1108
   420
    /* Scan program headers for memory range in use */
nkeynes@1108
   421
    for( i=0; i<head.e_phnum; i++ ) {
nkeynes@1108
   422
        if( phdr[i].p_type == PT_LOAD ) {
nkeynes@1108
   423
            if( phdr[i].p_vaddr < start )
nkeynes@1108
   424
                start = phdr[i].p_vaddr;
nkeynes@1108
   425
            if( phdr[i].p_vaddr + phdr[i].p_memsz > end )
nkeynes@1108
   426
                end = phdr[i].p_vaddr + phdr[i].p_memsz;
nkeynes@1108
   427
        }
nkeynes@1108
   428
    }
nkeynes@1108
   429
nkeynes@1108
   430
    if( start != BINARY_LOAD_ADDR ) {
nkeynes@1109
   431
        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
   432
        return FALSE;
nkeynes@1108
   433
    }
nkeynes@1108
   434
    if( end >= 0x8D000000 ) {
nkeynes@1109
   435
        SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 binary is too large to fit in memory (end address is %08X)", end );
nkeynes@1108
   436
        return FALSE;
nkeynes@1108
   437
    }
nkeynes@1108
   438
nkeynes@1108
   439
    /* Load the program into memory */
nkeynes@1108
   440
    char *program = g_malloc0( end-start );
nkeynes@1108
   441
    for( i=0; i<head.e_phnum; i++ ) {
nkeynes@1108
   442
        if( phdr[i].p_type == PT_LOAD ) {
nkeynes@1108
   443
            lseek( fd, phdr[i].p_offset, SEEK_SET );
nkeynes@1108
   444
            uint32_t size = MIN( phdr[i].p_filesz, phdr[i].p_memsz);
nkeynes@1117
   445
            int status = read( fd, program + phdr[i].p_vaddr - start, size );
nkeynes@1109
   446
            if( status == -1 ) {
nkeynes@1109
   447
                SET_ERROR( err, LX_ERR_FILE_IOERROR, "I/O error reading SH4 binary %s (%s)", filename, strerror(errno) );
nkeynes@1109
   448
            } else if( status != size ) {
nkeynes@1109
   449
                SET_ERROR( err, LX_ERR_FILE_IOERROR, "SH4 binary %s is corrupt", filename );
nkeynes@1109
   450
            }            
nkeynes@1108
   451
        }
nkeynes@1108
   452
    }
nkeynes@1108
   453
nkeynes@1108
   454
    /* And finally pass it over to the disc wrapper */
nkeynes@1108
   455
    return cdrom_disc_new_wrapped_binary(type, filename, program, end-start, err );
nkeynes@1108
   456
}
nkeynes@1108
   457
nkeynes@1109
   458
static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
nkeynes@1108
   459
{
nkeynes@1109
   460
    struct stat st;
nkeynes@1108
   461
    char *data;
nkeynes@1109
   462
    size_t len;
nkeynes@1109
   463
nkeynes@1109
   464
    if( fstat(fd, &st) == -1 ) {
nkeynes@1109
   465
        SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
nkeynes@1108
   466
        return NULL;
nkeynes@1108
   467
    }
nkeynes@1108
   468
nkeynes@1109
   469
    data = g_malloc(st.st_size);
nkeynes@1109
   470
    len = read( fd, data, st.st_size );
nkeynes@1109
   471
    if( len != st.st_size ) {
nkeynes@1109
   472
        SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
nkeynes@1109
   473
        free(data);
nkeynes@1108
   474
        return NULL;
nkeynes@1108
   475
    }
nkeynes@1108
   476
nkeynes@1109
   477
    return cdrom_disc_new_wrapped_binary( type, filename, data, st.st_size, err );
nkeynes@1108
   478
}
nkeynes@1109
   479
nkeynes@1109
   480
cdrom_disc_t cdrom_wrap_magic( cdrom_disc_type_t type, const gchar *filename, ERROR *err )
nkeynes@1109
   481
{
nkeynes@1109
   482
    cdrom_disc_t disc = NULL;
nkeynes@1109
   483
nkeynes@1109
   484
    int fd = open( filename, O_RDONLY );
nkeynes@1109
   485
    if( fd == -1 ) {
nkeynes@1109
   486
        SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s'", filename );
nkeynes@1109
   487
        return NULL;
nkeynes@1109
   488
    }
nkeynes@1109
   489
nkeynes@1109
   490
    lxdream_file_type_t filetype = file_identify( filename, fd, err );
nkeynes@1109
   491
    switch( filetype ) {
nkeynes@1109
   492
    case FILE_ELF:
nkeynes@1109
   493
        disc = cdrom_wrap_elf(type, filename, fd, err);
nkeynes@1109
   494
        break;
nkeynes@1109
   495
    case FILE_BINARY:
nkeynes@1109
   496
        disc = cdrom_wrap_binary(type, filename, fd, err);
nkeynes@1109
   497
        break;
nkeynes@1109
   498
    default:
nkeynes@1109
   499
        SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' cannot be wrapped (not a recognized binary)", filename );
nkeynes@1109
   500
        break;
nkeynes@1109
   501
    }
nkeynes@1109
   502
nkeynes@1109
   503
    close(fd);
nkeynes@1109
   504
    return disc;
nkeynes@1109
   505
nkeynes@1109
   506
}
.