filename | src/loader.c |
changeset | 1152:6464d890cc9e |
prev | 1117:0b14a8ec373b |
next | 1296:30ecee61f811 |
author | nkeynes |
date | Mon Feb 13 21:02:42 2012 +1000 (12 years ago) |
permissions | -rw-r--r-- |
last change | Move profile_block setting out of sh4x86 and back into sh4.c. Fix last bits preventing non-translation build |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * File loading routines, mostly for loading demos without going through the
5 * whole procedure of manually making a CD image for them.
6 *
7 * Copyright (c) 2005 Nathan Keynes.
8 *
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.
13 *
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.
18 */
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26 #include <stdint.h>
27 #include <glib/gstrfuncs.h>
28 #include <elf.h>
29 #include "mem.h"
30 #include "bootstrap.h"
31 #include "dreamcast.h"
32 #include "config.h"
33 #include "loader.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" },
43 { NULL, NULL } };
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 )
53 {
54 char buf[32];
55 lxdream_file_type_t result = FILE_UNKNOWN;
56 gboolean mustClose = FALSE;
57 off_t posn;
59 if( fd == -1 ) {
60 fd = open( filename, O_RDONLY );
61 if( fd == -1 ) {
62 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
63 return FILE_ERROR;
64 }
65 mustClose = TRUE;
66 } else {
67 /* Save current file position */
68 posn = lseek(fd, 0, SEEK_CUR);
69 if( posn == -1 ) {
70 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
71 return FILE_ERROR;
72 }
73 }
75 int status = read(fd, buf, 32);
76 if( status == -1 ) {
77 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
78 result = FILE_ERROR;
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' ) {
83 result = FILE_ELF;
84 } else if( memcmp( buf, "PK\x03\x04", 4 ) == 0 ) {
85 result = FILE_ZIP;
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 ) {
91 result = FILE_ISO;
92 } else {
93 /* Check the file extension - .bin = sh4 binary */
94 int len = strlen(filename);
95 struct stat st;
97 if( len > 4 && strcasecmp(filename + (len-4), ".bin") == 0 &&
98 fstat(fd, &st) != -1 && st.st_size <= BINARY_MAX_SIZE ) {
99 result = FILE_BINARY;
100 }
101 }
103 if( mustClose ) {
104 close(fd);
105 } else {
106 lseek( fd, posn, SEEK_SET );
107 }
108 return result;
109 }
112 gboolean file_load_exec( const gchar *filename, ERROR *err )
113 {
114 gboolean result = TRUE;
116 int fd = open( filename, O_RDONLY );
117 if( fd == -1 ) {
118 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
119 return FALSE;
120 }
122 lxdream_file_type_t type = file_identify(filename, fd, err);
123 switch( type ) {
124 case FILE_ERROR:
125 result = FALSE;
126 break;
127 case FILE_ELF:
128 result = file_load_elf( filename, fd, err );
129 break;
130 case FILE_BINARY:
131 result = file_load_binary( filename, fd, err );
132 break;
133 default:
134 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized as an executable binary", filename );
135 result = FALSE;
136 break;
137 }
139 close(fd);
140 return result;
141 }
143 lxdream_file_type_t file_load_magic( const gchar *filename, gboolean wrap_exec, ERROR *err )
144 {
145 gboolean result;
146 /* Try disc types first */
147 cdrom_disc_t disc = cdrom_disc_open( filename, err );
148 if( disc != NULL ) {
149 gdrom_mount_disc(disc);
150 return FILE_DISC;
151 } else if( err != LX_ERR_FILE_UNKNOWN ) {
152 return FILE_ERROR;
153 }
155 int fd = open( filename, O_RDONLY );
156 if( fd == -1 ) {
157 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
158 return FILE_ERROR;
159 }
161 lxdream_file_type_t type = file_identify(filename, fd, err);
162 switch( type ) {
163 case FILE_ERROR:
164 result = FALSE;
165 break;
166 case FILE_ELF:
167 if( wrap_exec ) {
168 disc = cdrom_wrap_elf( CDROM_DISC_XA, filename, fd, err );
169 result = disc != NULL;
170 if( disc != NULL ) {
171 gdrom_mount_disc(disc);
172 }
173 } else {
174 result = file_load_elf( filename, fd, err );
175 }
176 break;
177 case FILE_BINARY:
178 if( wrap_exec ) {
179 disc = cdrom_wrap_binary( CDROM_DISC_XA, filename, fd, err );
180 result = disc != NULL;
181 if( disc != NULL ) {
182 gdrom_mount_disc(disc);
183 }
184 } else {
185 result = file_load_binary( filename, fd, err );
186 }
187 break;
188 case FILE_SAVE_STATE:
189 result = dreamcast_load_state( filename );
190 break;
191 case FILE_ZIP:
192 SET_ERROR( err, LX_ERR_FILE_UNSUP, "ZIP/SBI not currently supported" );
193 result = FALSE;
194 break;
195 case FILE_ISO:
196 SET_ERROR( err, LX_ERR_FILE_UNSUP, "ISO files are not currently supported" );
197 result = FALSE;
198 break;
199 default:
200 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized", filename );
201 result = FALSE;
202 break;
203 }
204 close(fd);
205 if( result ) {
206 CLEAR_ERROR(err);
207 return type;
208 }
209 return FILE_ERROR;
210 }
212 void file_load_postload( const gchar *filename, int pc )
213 {
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
217 * correctly
218 */
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);
222 return;
223 }
224 }
225 dreamcast_program_loaded( filename, pc );
226 g_free(bootstrap_file);
227 }
230 static gboolean is_sh4_elf( Elf32_Ehdr *head )
231 {
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 );
238 }
240 static gboolean is_arm_elf( Elf32_Ehdr *head )
241 {
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 );
248 }
250 static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err )
251 {
252 Elf32_Ehdr head;
253 Elf32_Phdr phdr;
254 int i;
256 if( read( fd, &head, sizeof(head) ) != sizeof(head) )
257 return FALSE;
258 if( !is_sh4_elf(&head) ) {
259 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
260 return FALSE;
261 }
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 );
273 }
274 }
275 }
277 file_load_postload( filename, head.e_entry );
278 return TRUE;
279 }
281 static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err )
282 {
283 struct stat st;
285 if( fstat( fd, &st ) == -1 ) {
286 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
287 return FALSE;
288 }
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 );
292 return FALSE;
293 }
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) );
298 return FALSE;
299 }
301 file_load_postload( filename, BINARY_LOAD_ADDR );
302 return TRUE;
303 }
305 /**
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)
309 * @param bin_size
310 */
311 cdrom_disc_t cdrom_disc_new_wrapped_binary( cdrom_disc_type_t type, const gchar *filename, unsigned char *bin, size_t bin_size,
312 ERROR *err )
313 {
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' ) {
322 g_free(data);
323 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file is not configured" );
324 return NULL;
325 }
327 FILE *f = fopen( bootstrap_file, "ro" );
328 if( f == NULL ) {
329 g_free(data);
330 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' could not be opened", bootstrap_file );
331 return FALSE;
332 }
333 size_t len = fread( bootstrap, 1, 32768, f );
334 fclose(f);
335 if( len != 32768 ) {
336 g_free(data);
337 SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' is invalid", bootstrap_file );
338 return FALSE;
339 }
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 );
346 data = scramblebin;
347 start_lba = 0x2DB6; /* CDROM_START (does it matter?) */
348 g_free(bin);
349 }
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 );
354 char tmp[129];
355 int name_len = snprintf( tmp, 129, "lxdream wrapped image: %s", filename );
356 if( name_len < 128 )
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);
364 if( status != 1 ) {
365 g_free(data);
366 SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
367 return NULL;
368 }
370 IsoStream *stream;
371 if( iso_mem_stream_new(data, bin_size, &stream) != 1 ) {
372 g_free(data);
373 iso_image_unref(iso);
374 SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
375 return NULL;
376 }
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,
379 bootstrap, err );
380 if( track == NULL ) {
381 iso_image_unref(iso);
382 return NULL;
383 }
385 cdrom_disc_t disc = cdrom_disc_new_from_track( type, track, start_lba, err );
386 iso_image_unref(iso);
387 if( disc != NULL ) {
388 disc->name = g_strdup(filename);
389 }
390 return disc;
391 }
393 static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
394 {
395 Elf32_Ehdr head;
396 int i;
398 /* Check the file header is actually an SH4 binary */
399 if( read( fd, &head, sizeof(head) ) != sizeof(head) )
400 return FALSE;
402 if( !is_sh4_elf(&head) ) {
403 SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
404 return FALSE;
405 }
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 );
408 return FALSE;
409 }
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" );
416 return FALSE;
417 }
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;
427 }
428 }
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 );
432 return FALSE;
433 }
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 );
436 return FALSE;
437 }
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 );
446 if( status == -1 ) {
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 );
450 }
451 }
452 }
454 /* And finally pass it over to the disc wrapper */
455 return cdrom_disc_new_wrapped_binary(type, filename, program, end-start, err );
456 }
458 static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
459 {
460 struct stat st;
461 char *data;
462 size_t len;
464 if( fstat(fd, &st) == -1 ) {
465 SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
466 return NULL;
467 }
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) );
473 free(data);
474 return NULL;
475 }
477 return cdrom_disc_new_wrapped_binary( type, filename, data, st.st_size, err );
478 }
480 cdrom_disc_t cdrom_wrap_magic( cdrom_disc_type_t type, const gchar *filename, ERROR *err )
481 {
482 cdrom_disc_t disc = NULL;
484 int fd = open( filename, O_RDONLY );
485 if( fd == -1 ) {
486 SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s'", filename );
487 return NULL;
488 }
490 lxdream_file_type_t filetype = file_identify( filename, fd, err );
491 switch( filetype ) {
492 case FILE_ELF:
493 disc = cdrom_wrap_elf(type, filename, fd, err);
494 break;
495 case FILE_BINARY:
496 disc = cdrom_wrap_binary(type, filename, fd, err);
497 break;
498 default:
499 SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' cannot be wrapped (not a recognized binary)", filename );
500 break;
501 }
503 close(fd);
504 return disc;
506 }
.