4 * File loading routines, mostly for loading demos without going through the
5 * whole procedure of manually making a CD image for them.
7 * Copyright (c) 2005 Nathan Keynes.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
27 #include <glib/gstrfuncs.h>
30 #include "bootstrap.h"
31 #include "dreamcast.h"
34 #include "drivers/cdrom/cdrom.h"
35 #include "drivers/cdrom/isofs.h"
36 #include "gdrom/gdrom.h"
38 const char bootstrap_magic[32] = "SEGA SEGAKATANA SEGA ENTERPRISES";
39 const char iso_magic[6] = "\001CD001";
40 char *file_loader_extensions[][2] = {
41 { "sbi", "Self Boot Inducer" },
42 { "bin", "SH4 Bin file" },
45 static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err );
46 static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err );
47 static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err );
48 static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err );
52 lxdream_file_type_t file_identify( const gchar *filename, int fd, ERROR *err )
55 lxdream_file_type_t result = FILE_UNKNOWN;
56 gboolean mustClose = FALSE;
60 fd = open( filename, O_RDONLY );
62 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
67 /* Save current file position */
68 posn = lseek(fd, 0, SEEK_CUR);
70 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
75 int status = read(fd, buf, 32);
77 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
79 } else if( status != 32 ) {
80 result = FILE_UNKNOWN;
81 } else if( buf[0] == 0x7F && buf[1] == 'E' &&
82 buf[2] == 'L' && buf[3] == 'F' ) {
84 } else if( memcmp( buf, "PK\x03\x04", 4 ) == 0 ) {
86 } else if( memcmp( buf, DREAMCAST_SAVE_MAGIC, 16 ) == 0 ) {
87 result = FILE_SAVE_STATE;
88 } else if( lseek( fd, 32768, SEEK_SET ) == 32768 &&
89 read( fd, buf, 8 ) == 8 &&
90 memcmp( buf, iso_magic, 6) == 0 ) {
93 /* Check the file extension - .bin = sh4 binary */
94 int len = strlen(filename);
97 if( len > 4 && strcasecmp(filename + (len-4), ".bin") == 0 &&
98 fstat(fd, &st) != -1 && st.st_size <= BINARY_MAX_SIZE ) {
106 lseek( fd, posn, SEEK_SET );
112 gboolean file_load_exec( const gchar *filename, ERROR *err )
114 gboolean result = TRUE;
116 int fd = open( filename, O_RDONLY );
118 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
122 lxdream_file_type_t type = file_identify(filename, fd, err);
128 result = file_load_elf( filename, fd, err );
131 result = file_load_binary( filename, fd, err );
134 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized as an executable binary", filename );
143 lxdream_file_type_t file_load_magic( const gchar *filename, gboolean wrap_exec, ERROR *err )
146 /* Try disc types first */
147 cdrom_disc_t disc = cdrom_disc_open( filename, err );
149 gdrom_mount_disc(disc);
151 } else if( err != LX_ERR_FILE_UNKNOWN ) {
155 int fd = open( filename, O_RDONLY );
157 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
161 lxdream_file_type_t type = file_identify(filename, fd, err);
168 disc = cdrom_wrap_elf( CDROM_DISC_XA, filename, fd, err );
169 result = disc != NULL;
171 gdrom_mount_disc(disc);
174 result = file_load_elf( filename, fd, err );
179 disc = cdrom_wrap_binary( CDROM_DISC_XA, filename, fd, err );
180 result = disc != NULL;
182 gdrom_mount_disc(disc);
185 result = file_load_binary( filename, fd, err );
188 case FILE_SAVE_STATE:
189 result = dreamcast_load_state( filename );
192 SET_ERROR( err, LX_ERR_FILE_UNSUP, "ZIP/SBI not currently supported" );
196 SET_ERROR( err, LX_ERR_FILE_UNSUP, "ISO files are not currently supported" );
200 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized", filename );
212 void file_load_postload( const gchar *filename, int pc )
214 gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
215 if( bootstrap_file != NULL && bootstrap_file[0] != '\0' ) {
216 /* Load in a bootstrap before the binary, to initialize everything
219 if( mem_load_block( bootstrap_file, BOOTSTRAP_LOAD_ADDR, BOOTSTRAP_SIZE ) == 0 ) {
220 dreamcast_program_loaded( filename, BOOTSTRAP_ENTRY_ADDR );
221 g_free(bootstrap_file);
225 dreamcast_program_loaded( filename, pc );
226 g_free(bootstrap_file);
230 static gboolean is_sh4_elf( Elf32_Ehdr *head )
232 return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
233 head->e_ident[EI_DATA] == ELFDATA2LSB &&
234 head->e_ident[EI_VERSION] == 1 &&
235 head->e_type == ET_EXEC &&
236 head->e_machine == EM_SH &&
237 head->e_version == 1 );
240 static gboolean is_arm_elf( Elf32_Ehdr *head )
242 return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
243 head->e_ident[EI_DATA] == ELFDATA2LSB &&
244 head->e_ident[EI_VERSION] == 1 &&
245 head->e_type == ET_EXEC &&
246 head->e_machine == EM_ARM &&
247 head->e_version == 1 );
250 static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err )
256 if( read( fd, &head, sizeof(head) ) != sizeof(head) )
258 if( !is_sh4_elf(&head) ) {
259 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
263 /* Program headers */
264 for( i=0; i<head.e_phnum; i++ ) {
265 lseek( fd, head.e_phoff + i*head.e_phentsize, SEEK_SET );
266 read( fd, &phdr, sizeof(phdr) );
267 if( phdr.p_type == PT_LOAD ) {
268 lseek( fd, phdr.p_offset, SEEK_SET );
269 sh4ptr_t target = mem_get_region( phdr.p_vaddr );
270 read( fd, target, phdr.p_filesz );
271 if( phdr.p_memsz > phdr.p_filesz ) {
272 memset( target + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz );
277 file_load_postload( filename, head.e_entry );
281 static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err )
285 if( fstat( fd, &st ) == -1 ) {
286 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
290 if( st.st_size > BINARY_MAX_SIZE ) {
291 SET_ERROR( err, LX_ERR_FILE_INVALID, "Binary file '%s' is too large to fit in memory", filename );
295 sh4ptr_t target = mem_get_region( BINARY_LOAD_ADDR );
296 if( read( fd, target, st.st_size ) != st.st_size ) {
297 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
301 file_load_postload( filename, BINARY_LOAD_ADDR );
306 * Create a new CDROM disc containing a single 1ST_READ.BIN.
307 * @param type The disc type - must be CDROM_DISC_GDROM or CDROM_DISC_XA
308 * @param bin The binary data (takes ownership)
311 cdrom_disc_t cdrom_disc_new_wrapped_binary( cdrom_disc_type_t type, const gchar *filename, unsigned char *bin, size_t bin_size,
314 IsoImage *iso = NULL;
315 unsigned char *data = bin;
316 cdrom_lba_t start_lba = 45000; /* GDROM_START */
317 char bootstrap[32768];
319 /* 1. Load in the bootstrap: Note failures here are considered configuration errors */
320 gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
321 if( bootstrap_file == NULL || bootstrap_file[0] == '\0' ) {
323 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file is not configured" );
327 FILE *f = fopen( bootstrap_file, "ro" );
330 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' could not be opened", bootstrap_file );
333 size_t len = fread( bootstrap, 1, 32768, f );
337 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' is invalid", bootstrap_file );
341 /* 2. Scramble the binary if necessary (and set type settings) */
342 if( type != CDROM_DISC_GDROM ) {
343 /* scramble the binary if we're going the MIL-CD route */
344 unsigned char *scramblebin = g_malloc(bin_size);
345 bootprogram_scramble( scramblebin, bin, bin_size );
347 start_lba = 0x2DB6; /* CDROM_START (does it matter?) */
351 /* 3. Frob the bootstrap data */
352 dc_bootstrap_head_t boot_header = (dc_bootstrap_head_t)bootstrap;
353 memcpy( boot_header->boot_file, "1ST_READ.BIN ", 16 );
355 int name_len = snprintf( tmp, 129, "lxdream wrapped image: %s", filename );
357 memset( tmp+name_len, ' ', 128-name_len );
358 memcpy( boot_header->product_name, tmp, 128 );
359 // bootstrap_update_crc(bootstrap);
362 /* 4. Build the ISO image */
363 int status = iso_image_new("autocd", &iso);
366 SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
371 if( iso_mem_stream_new(data, bin_size, &stream) != 1 ) {
373 iso_image_unref(iso);
374 SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
377 iso_tree_add_new_file(iso_image_get_root(iso), "1ST_READ.BIN", stream, NULL);
378 sector_source_t track = iso_sector_source_new( iso, SECTOR_MODE2_FORM1, start_lba,
380 if( track == NULL ) {
381 iso_image_unref(iso);
385 cdrom_disc_t disc = cdrom_disc_new_from_track( type, track, start_lba, err );
386 iso_image_unref(iso);
388 disc->name = g_strdup(filename);
393 static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
398 /* Check the file header is actually an SH4 binary */
399 if( read( fd, &head, sizeof(head) ) != sizeof(head) )
402 if( !is_sh4_elf(&head) ) {
403 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
406 if( head.e_entry != BINARY_LOAD_ADDR ) {
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 );
411 /* Load the program headers */
412 Elf32_Phdr phdr[head.e_phnum];
413 lseek( fd, head.e_phoff, SEEK_SET );
414 if( read( fd, phdr, sizeof(phdr) ) != sizeof(phdr) ) {
415 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not a valid executable file" );
419 sh4addr_t start = (sh4addr_t)-1, end=0;
420 /* Scan program headers for memory range in use */
421 for( i=0; i<head.e_phnum; i++ ) {
422 if( phdr[i].p_type == PT_LOAD ) {
423 if( phdr[i].p_vaddr < start )
424 start = phdr[i].p_vaddr;
425 if( phdr[i].p_vaddr + phdr[i].p_memsz > end )
426 end = phdr[i].p_vaddr + phdr[i].p_memsz;
430 if( start != BINARY_LOAD_ADDR ) {
431 SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 Binary has incorrect load address (should be %08X but is %08X)", BINARY_LOAD_ADDR, start );
434 if( end >= 0x8D000000 ) {
435 SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 binary is too large to fit in memory (end address is %08X)", end );
439 /* Load the program into memory */
440 char *program = g_malloc0( end-start );
441 for( i=0; i<head.e_phnum; i++ ) {
442 if( phdr[i].p_type == PT_LOAD ) {
443 lseek( fd, phdr[i].p_offset, SEEK_SET );
444 uint32_t size = MIN( phdr[i].p_filesz, phdr[i].p_memsz);
445 int status = read( fd, program + phdr[i].p_vaddr - start, size );
447 SET_ERROR( err, LX_ERR_FILE_IOERROR, "I/O error reading SH4 binary %s (%s)", filename, strerror(errno) );
448 } else if( status != size ) {
449 SET_ERROR( err, LX_ERR_FILE_IOERROR, "SH4 binary %s is corrupt", filename );
454 /* And finally pass it over to the disc wrapper */
455 return cdrom_disc_new_wrapped_binary(type, filename, program, end-start, err );
458 static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
464 if( fstat(fd, &st) == -1 ) {
465 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
469 data = g_malloc(st.st_size);
470 len = read( fd, data, st.st_size );
471 if( len != st.st_size ) {
472 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
477 return cdrom_disc_new_wrapped_binary( type, filename, data, st.st_size, err );
480 cdrom_disc_t cdrom_wrap_magic( cdrom_disc_type_t type, const gchar *filename, ERROR *err )
482 cdrom_disc_t disc = NULL;
484 int fd = open( filename, O_RDONLY );
486 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s'", filename );
490 lxdream_file_type_t filetype = file_identify( filename, fd, err );
493 disc = cdrom_wrap_elf(type, filename, fd, err);
496 disc = cdrom_wrap_binary(type, filename, fd, err);
499 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' cannot be wrapped (not a recognized binary)", filename );
.