From 8e971eaf5ab75b80e6bc1a2d3beaf30ab084b4d8 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sun, 27 Mar 2022 13:37:06 -0300 Subject: [PATCH] Virtual ISO: Add El Torito boot image loading for both emulation and non-emulation modes --- src/cdrom/cdrom_image_viso.c | 140 +++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 8 deletions(-) diff --git a/src/cdrom/cdrom_image_viso.c b/src/cdrom/cdrom_image_viso.c index 1377c9431..72ccd89c5 100644 --- a/src/cdrom/cdrom_image_viso.c +++ b/src/cdrom/cdrom_image_viso.c @@ -90,13 +90,13 @@ typedef struct _viso_entry_ { } viso_entry_t; typedef struct { - uint64_t vol_size_offsets[2], pt_meta_offsets[2]; + uint64_t vol_size_offsets[2], pt_meta_offsets[2], eltorito_offset; uint32_t metadata_sectors, all_sectors, entry_map_size; unsigned int sector_size, file_fifo_pos; uint8_t *metadata; track_file_t tf; - viso_entry_t root_dir, **entry_map, *file_fifo[VISO_OPEN_FILES]; + viso_entry_t root_dir, *eltorito_entry, **entry_map, *file_fifo[VISO_OPEN_FILES]; } viso_t; #define ENABLE_CDROM_IMAGE_VISO_LOG 1 @@ -660,6 +660,10 @@ viso_init(const char *dirname, int *error) viso->entry_map_size++; /* round up to the next sector */ } + /* Detect El Torito boot code file and set it accordingly. */ + if (!strnicmp(readdir_entry->d_name, "eltorito.", 9) && (!stricmp(readdir_entry->d_name + 9, "com") || !stricmp(readdir_entry->d_name + 9, "img"))) + viso->eltorito_entry = last_entry; + /* Set short filename. */ if (viso_get_short_filename(dir, last_entry->name_short, readdir_entry->d_name)) goto end; @@ -821,6 +825,29 @@ next_dir: /* Write volume descriptor. */ fwrite(data, viso->sector_size, 1, viso->tf.file); + + /* Write El Torito boot descriptor. This is an awkward spot for + that, but the spec requires it to be the second descriptor. */ + if (!i && viso->eltorito_entry) { + p = data; + *p++ = 0; /* type */ + memcpy(p, "CD001", 5); /* standard ID */ + p += 5; + *p++ = 1; /* version */ + + memcpy(p, "EL TORITO SPECIFICATION", 24); /* identifier */ + p += 24; + VISO_SKIP(p, 40); + + /* Save the boot catalog pointer's offset for later. */ + viso->eltorito_offset = ftello64(viso->tf.file) + (p - data); + + /* Blank the rest of the working sector. */ + memset(p, 0x00, viso->sector_size - (p - data)); + + /* Write boot descriptor. */ + fwrite(data, viso->sector_size, 1, viso->tf.file); + } } /* Fill terminator. */ @@ -845,6 +872,82 @@ next_dir: fwrite(data, write, 1, viso->tf.file); } + /* Handle El Torito boot catalog. */ + if (viso->eltorito_entry) { + /* Write a pointer to this boot catalog to the boot descriptor. */ + *((uint32_t *) data) = ftello64(viso->tf.file) / viso->sector_size; + viso_pwrite(data, viso->eltorito_offset, 4, 1, viso->tf.file); + + /* Fill boot catalog validation entry. */ + p = data; + *p++ = 0x01; /* header ID */ + *p++ = 0x00; /* platform */ + *p++ = 0x00; /* reserved */ + *p++ = 0x00; + VISO_SKIP(p, 24); + strncpy((char *) (p - 24), EMU_NAME, 24); /* ID string */ + *p++ = 0x00; /* checksum */ + *p++ = 0x00; + *p++ = 0x55; /* key bytes */ + *p++ = 0xaa; + + /* Calculate checksum. */ + uint16_t eltorito_checksum = 0; + for (int i = 0; i < (p - data); i += 2) + eltorito_checksum -= *((uint16_t *) &data[i]); + *((uint16_t *) &data[28]) = eltorito_checksum; + + /* Now fill the default boot entry. */ + *p++ = 0x88; /* bootable flag */ + + if (viso->eltorito_entry->name_short[9] == 'C') { /* boot media type */ + *p++ = 0x00; + } else { + /* This could use with a decoupling of fdd_img's algorithms + for loading non-raw images and detecting raw image sizes. */ + switch (viso->eltorito_entry->stats.st_size) { + case 0 ... 1228800: /* 1.2 MB */ + *p++ = 0x01; + break; + + case 1228801 ... 1474560: /* 1.44 MB */ + *p++ = 0x02; + break; + + case 1474561 ... 2949120: /* 2.88 MB */ + *p++ = 0x03; + break; + + default: /* hard drive */ + *p++ = 0x04; + break; + } + } + + *p++ = 0x00; /* load segment */ + *p++ = 0x00; + *p++ = 0x00; /* system type (is this even relevant?) */ + *p++ = 0x00; /* reserved */ + + /* Save offsets to the boot catalog entry's offset and size fields for later. */ + viso->eltorito_offset = ftello64(viso->tf.file) + (p - data); + + /* Blank the rest of the working sector. This includes the sector count, + ISO sector offset and 20-byte selection criteria fields at the end. */ + memset(p, 0x00, viso->sector_size - (p - data)); + + /* Write boot catalog. */ + fwrite(data, viso->sector_size, 1, viso->tf.file); + + /* Pad to the next even sector. */ + write = ftello64(viso->tf.file) % (viso->sector_size * 2); + if (write) { + write = (viso->sector_size * 2) - write; + memset(data, 0x00, write); + fwrite(data, write, 1, viso->tf.file); + } + } + /* Write each path table. */ for (int i = 0; i < 4; i++) { cdrom_image_viso_log("VISO: Generating path table #%d:\n", i); @@ -960,6 +1063,10 @@ next_dir: viso_entry_t *entry = dir->first_child; int dir_type = VISO_DIR_CURRENT; while (entry) { + /* Skip the El Torito boot code entry if present. */ + if (entry == viso->eltorito_entry) + goto next_entry; + cdrom_image_viso_log("[%08X] %s => %s\n", entry, entry->path ? entry->path : ((dir_type == VISO_DIR_PARENT) ? dir->parent->path : dir->path), i ? entry->name_rr : entry->name_short); @@ -996,7 +1103,7 @@ next_dir: /* Write entry. */ fwrite(data, data[0], 1, viso->tf.file); - +next_entry: /* Move on to the next entry, and stop if the end of this directory was reached. */ entry = entry->next; if (entry && (entry->parent != dir)) @@ -1045,11 +1152,28 @@ next_dir: continue; } - /* Write this file's starting sector offset to its directory entries. */ - p = data; - VISO_LBE_32(p, viso->all_sectors); - for (int i = 0; i < (sizeof(entry->dr_offsets) / sizeof(entry->dr_offsets[0])); i++) - viso_pwrite(data, entry->dr_offsets[i] + 2, 8, 1, viso->tf.file); + /* Write this file's starting sector offset to its directory + entries, unless this is the El Torito boot code entry, + in which case, write offset and size to the boot entry. */ + if (entry == viso->eltorito_entry) { + /* Load the entire file if not emulating, or just the first virtual + sector (which usually contains all the boot code) if emulating. */ + if (entry->name_short[9] == 'C') { + uint32_t boot_size = entry->stats.st_size; + if (boot_size % 512) /* round up */ + boot_size += 512 - (boot_size % 512); + *((uint16_t *) &data[0]) = boot_size / 512; + } else { + *((uint16_t *) &data[0]) = 1; + } + *((uint32_t *) &data[2]) = viso->all_sectors; + viso_pwrite(data, viso->eltorito_offset, 6, 1, viso->tf.file); + } else { + p = data; + VISO_LBE_32(p, viso->all_sectors); + for (int i = 0; i < (sizeof(entry->dr_offsets) / sizeof(entry->dr_offsets[0])); i++) + viso_pwrite(data, entry->dr_offsets[i] + 2, 8, 1, viso->tf.file); + } /* Save this file's starting offset. This overwrites dr_offsets in the union. */ entry->data_offset = ((uint64_t) viso->all_sectors) * viso->sector_size;