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.
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( !IS_ERROR_CODE(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);
229 static gboolean is_arch( Elf32_Ehdr *head, Elf32_Half mach ) {
230 return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
231 head->e_ident[EI_DATA] == ELFDATA2LSB &&
232 head->e_ident[EI_VERSION] == 1 &&
233 head->e_type == ET_EXEC &&
234 head->e_machine == mach &&
235 head->e_version == 1 );
238 #define is_sh4_elf(head) is_arch(head, EM_SH)
239 #define is_arm_elf(head) is_arch(head, EM_ARM)
241 static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err )
247 if( read( fd, &head, sizeof(head) ) != sizeof(head) )
249 if( !is_sh4_elf(&head) ) {
250 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
254 /* Program headers */
255 for( i=0; i<head.e_phnum; i++ ) {
256 lseek( fd, head.e_phoff + i*head.e_phentsize, SEEK_SET );
257 read( fd, &phdr, sizeof(phdr) );
258 if( phdr.p_type == PT_LOAD ) {
259 lseek( fd, phdr.p_offset, SEEK_SET );
260 sh4ptr_t target = mem_get_region( phdr.p_vaddr );
261 read( fd, target, phdr.p_filesz );
262 if( phdr.p_memsz > phdr.p_filesz ) {
263 memset( target + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz );
268 file_load_postload( filename, head.e_entry );
272 static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err )
276 if( fstat( fd, &st ) == -1 ) {
277 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
281 if( st.st_size > BINARY_MAX_SIZE ) {
282 SET_ERROR( err, LX_ERR_FILE_INVALID, "Binary file '%s' is too large to fit in memory", filename );
286 sh4ptr_t target = mem_get_region( BINARY_LOAD_ADDR );
287 if( read( fd, target, st.st_size ) != st.st_size ) {
288 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
292 file_load_postload( filename, BINARY_LOAD_ADDR );
297 * Create a new CDROM disc containing a single 1ST_READ.BIN.
298 * @param type The disc type - must be CDROM_DISC_GDROM or CDROM_DISC_XA
299 * @param bin The binary data (takes ownership)
302 cdrom_disc_t cdrom_disc_new_wrapped_binary( cdrom_disc_type_t type, const gchar *filename, unsigned char *bin, size_t bin_size,
305 IsoImage *iso = NULL;
306 unsigned char *data = bin;
307 cdrom_lba_t start_lba = 45000; /* GDROM_START */
308 char bootstrap[32768];
310 /* 1. Load in the bootstrap: Note failures here are considered configuration errors */
311 gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
312 if( bootstrap_file == NULL || bootstrap_file[0] == '\0' ) {
314 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file is not configured" );
318 FILE *f = fopen( bootstrap_file, "ro" );
321 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' could not be opened", bootstrap_file );
324 size_t len = fread( bootstrap, 1, 32768, f );
328 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' is invalid", bootstrap_file );
332 /* 2. Scramble the binary if necessary (and set type settings) */
333 if( type != CDROM_DISC_GDROM ) {
334 /* scramble the binary if we're going the MIL-CD route */
335 unsigned char *scramblebin = g_malloc(bin_size);
336 bootprogram_scramble( scramblebin, bin, bin_size );
338 start_lba = 0x2DB6; /* CDROM_START (does it matter?) */
342 /* 3. Frob the bootstrap data */
343 dc_bootstrap_head_t boot_header = (dc_bootstrap_head_t)bootstrap;
344 memcpy( boot_header->boot_file, "1ST_READ.BIN ", 16 );
346 int name_len = snprintf( tmp, 129, "lxdream wrapped image: %s", filename );
348 memset( tmp+name_len, ' ', 128-name_len );
349 memcpy( boot_header->product_name, tmp, 128 );
350 // bootstrap_update_crc(bootstrap);
353 /* 4. Build the ISO image */
354 int status = iso_image_new("autocd", &iso);
357 SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
362 if( iso_mem_stream_new(data, bin_size, &stream) != 1 ) {
364 iso_image_unref(iso);
365 SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
368 iso_tree_add_new_file(iso_image_get_root(iso), "1ST_READ.BIN", stream, NULL);
369 sector_source_t track = iso_sector_source_new( iso, SECTOR_MODE2_FORM1, start_lba,
371 if( track == NULL ) {
372 iso_image_unref(iso);
376 cdrom_disc_t disc = cdrom_disc_new_from_track( type, track, start_lba, err );
377 iso_image_unref(iso);
379 disc->name = g_strdup(filename);
384 static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
389 /* Check the file header is actually an SH4 binary */
390 if( read( fd, &head, sizeof(head) ) != sizeof(head) )
393 if( !is_sh4_elf(&head) ) {
394 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
397 if( head.e_entry != BINARY_LOAD_ADDR ) {
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 );
402 /* Load the program headers */
403 Elf32_Phdr phdr[head.e_phnum];
404 lseek( fd, head.e_phoff, SEEK_SET );
405 if( read( fd, phdr, sizeof(phdr) ) != sizeof(phdr) ) {
406 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not a valid executable file" );
410 sh4addr_t start = (sh4addr_t)-1, end=0;
411 /* Scan program headers for memory range in use */
412 for( i=0; i<head.e_phnum; i++ ) {
413 if( phdr[i].p_type == PT_LOAD ) {
414 if( phdr[i].p_vaddr < start )
415 start = phdr[i].p_vaddr;
416 if( phdr[i].p_vaddr + phdr[i].p_memsz > end )
417 end = phdr[i].p_vaddr + phdr[i].p_memsz;
421 if( start != BINARY_LOAD_ADDR ) {
422 SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 Binary has incorrect load address (should be %08X but is %08X)", BINARY_LOAD_ADDR, start );
425 if( end >= 0x8D000000 ) {
426 SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 binary is too large to fit in memory (end address is %08X)", end );
430 /* Load the program into memory */
431 unsigned char *program = g_malloc0( end-start );
432 for( i=0; i<head.e_phnum; i++ ) {
433 if( phdr[i].p_type == PT_LOAD ) {
434 lseek( fd, phdr[i].p_offset, SEEK_SET );
435 uint32_t size = MIN( phdr[i].p_filesz, phdr[i].p_memsz);
436 int status = read( fd, program + phdr[i].p_vaddr - start, size );
438 SET_ERROR( err, LX_ERR_FILE_IOERROR, "I/O error reading SH4 binary %s (%s)", filename, strerror(errno) );
439 } else if( status != size ) {
440 SET_ERROR( err, LX_ERR_FILE_IOERROR, "SH4 binary %s is corrupt", filename );
445 /* And finally pass it over to the disc wrapper */
446 return cdrom_disc_new_wrapped_binary(type, filename, program, end-start, err );
449 static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
455 if( fstat(fd, &st) == -1 ) {
456 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
460 data = g_malloc(st.st_size);
461 len = read( fd, data, st.st_size );
462 if( len != st.st_size ) {
463 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
468 return cdrom_disc_new_wrapped_binary( type, filename, data, st.st_size, err );
471 cdrom_disc_t cdrom_wrap_magic( cdrom_disc_type_t type, const gchar *filename, ERROR *err )
473 cdrom_disc_t disc = NULL;
475 int fd = open( filename, O_RDONLY );
477 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s'", filename );
481 lxdream_file_type_t filetype = file_identify( filename, fd, err );
484 disc = cdrom_wrap_elf(type, filename, fd, err);
487 disc = cdrom_wrap_binary(type, filename, fd, err);
490 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' cannot be wrapped (not a recognized binary)", filename );
.