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