2022-03-26 22:17:09 -03:00
|
|
|
/*
|
|
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of the 86Box distribution.
|
|
|
|
|
*
|
|
|
|
|
* Virtual ISO CD-ROM image back-end.
|
|
|
|
|
*
|
|
|
|
|
* Authors: RichardG <richardg867@gmail.com>
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2022 RichardG.
|
|
|
|
|
*/
|
|
|
|
|
// clang-format off
|
|
|
|
|
#define _LARGEFILE_SOURCE
|
|
|
|
|
#define _LARGEFILE64_SOURCE
|
|
|
|
|
#define __STDC_FORMAT_MACROS
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#include <wchar.h>
|
|
|
|
|
#define HAVE_STDARG_H
|
|
|
|
|
#include <86box/86box.h>
|
|
|
|
|
#include <86box/bswap.h>
|
|
|
|
|
#include <86box/cdrom_image_backend.h>
|
|
|
|
|
#include <86box/plat.h>
|
|
|
|
|
#include <86box/plat_dir.h>
|
|
|
|
|
#include <86box/version.h>
|
|
|
|
|
#include <86box/timer.h>
|
|
|
|
|
#include <86box/nvr.h>
|
|
|
|
|
// clang-format on
|
|
|
|
|
|
2022-03-27 16:27:41 -03:00
|
|
|
#define VISO_SKIP(p, n) \
|
|
|
|
|
{ \
|
|
|
|
|
memset(p, 0x00, n); \
|
|
|
|
|
p += n; \
|
|
|
|
|
}
|
2022-03-31 12:54:46 -03:00
|
|
|
#define VISO_TIME_VALID(t) (((t) - 1) < ((time_t) -2))
|
2022-03-26 22:17:09 -03:00
|
|
|
|
2022-03-28 22:49:40 -03:00
|
|
|
/* ISO 9660 defines "both endian" data formats, which
|
|
|
|
|
are stored as little endian followed by big endian. */
|
2022-03-27 16:27:41 -03:00
|
|
|
#define VISO_LBE_16(p, x) \
|
|
|
|
|
{ \
|
|
|
|
|
*((uint16_t *) p) = cpu_to_le16(x); \
|
|
|
|
|
p += 2; \
|
|
|
|
|
*((uint16_t *) p) = cpu_to_be16(x); \
|
|
|
|
|
p += 2; \
|
|
|
|
|
}
|
|
|
|
|
#define VISO_LBE_32(p, x) \
|
|
|
|
|
{ \
|
|
|
|
|
*((uint32_t *) p) = cpu_to_le32(x); \
|
|
|
|
|
p += 4; \
|
|
|
|
|
*((uint32_t *) p) = cpu_to_be32(x); \
|
|
|
|
|
p += 4; \
|
|
|
|
|
}
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
#define VISO_SECTOR_SIZE COOKED_SECTOR_SIZE
|
|
|
|
|
#define VISO_OPEN_FILES 32
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
VISO_CHARSET_D = 0,
|
|
|
|
|
VISO_CHARSET_A,
|
|
|
|
|
VISO_CHARSET_FN,
|
|
|
|
|
VISO_CHARSET_ANY
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
VISO_DIR_CURRENT = 0,
|
2022-03-27 16:27:41 -03:00
|
|
|
VISO_DIR_CURRENT_ROOT,
|
|
|
|
|
VISO_DIR_PARENT,
|
2022-03-26 22:17:09 -03:00
|
|
|
VISO_DIR_REGULAR,
|
|
|
|
|
VISO_DIR_JOLIET
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct _viso_entry_ {
|
2022-03-27 16:27:41 -03:00
|
|
|
char *path, name_short[13], name_rr[257]; /* name_rr size limited by at least Linux */
|
|
|
|
|
union { /* save some memory */
|
2022-03-26 22:17:09 -03:00
|
|
|
uint64_t pt_offsets[4];
|
|
|
|
|
FILE *file;
|
|
|
|
|
};
|
2022-03-27 00:27:51 -03:00
|
|
|
union {
|
|
|
|
|
uint64_t dr_offsets[2];
|
|
|
|
|
uint64_t data_offset;
|
|
|
|
|
};
|
|
|
|
|
uint16_t name_joliet[111], pt_idx; /* name_joliet size limited by maximum directory record size */
|
2022-03-27 16:27:41 -03:00
|
|
|
uint8_t name_rr_len, name_joliet_len;
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
struct stat stats;
|
|
|
|
|
|
|
|
|
|
struct _viso_entry_ *parent, *next, *next_dir, *first_child;
|
|
|
|
|
} viso_entry_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2022-03-27 13:37:06 -03:00
|
|
|
uint64_t vol_size_offsets[2], pt_meta_offsets[2], eltorito_offset;
|
2022-03-26 22:17:09 -03:00
|
|
|
uint32_t metadata_sectors, all_sectors, entry_map_size;
|
|
|
|
|
unsigned int sector_size, file_fifo_pos;
|
|
|
|
|
uint8_t *metadata;
|
|
|
|
|
|
|
|
|
|
track_file_t tf;
|
2022-03-27 13:37:06 -03:00
|
|
|
viso_entry_t root_dir, *eltorito_entry, **entry_map, *file_fifo[VISO_OPEN_FILES];
|
2022-03-26 22:17:09 -03:00
|
|
|
} viso_t;
|
|
|
|
|
|
2022-03-27 16:27:41 -03:00
|
|
|
static const char rr_eid[] = "RRIP_1991A"; /* identifiers used in ER field for Rock Ridge */
|
|
|
|
|
static const char rr_edesc[] = "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS.";
|
2022-03-28 23:48:22 -03:00
|
|
|
static int8_t tz_offset = 0;
|
2022-03-27 16:27:41 -03:00
|
|
|
|
2022-03-26 22:17:09 -03:00
|
|
|
#define ENABLE_CDROM_IMAGE_VISO_LOG 1
|
|
|
|
|
#ifdef ENABLE_CDROM_IMAGE_VISO_LOG
|
|
|
|
|
int cdrom_image_viso_do_log = ENABLE_CDROM_IMAGE_VISO_LOG;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cdrom_image_viso_log(const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
if (cdrom_image_viso_do_log) {
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
pclog_ex(fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
# define cdrom_image_viso_log(fmt, ...)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
viso_pread(void *ptr, uint64_t offset, size_t size, size_t count, FILE *stream)
|
|
|
|
|
{
|
|
|
|
|
uint64_t cur_pos = ftello64(stream);
|
|
|
|
|
size_t ret = 0;
|
|
|
|
|
if (fseeko64(stream, offset, SEEK_SET) != -1)
|
|
|
|
|
ret = fread(ptr, size, count, stream);
|
|
|
|
|
fseeko64(stream, cur_pos, SEEK_SET);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
viso_pwrite(const void *ptr, uint64_t offset, size_t size, size_t count, FILE *stream)
|
|
|
|
|
{
|
|
|
|
|
uint64_t cur_pos = ftello64(stream);
|
|
|
|
|
size_t ret = 0;
|
|
|
|
|
if (fseeko64(stream, offset, SEEK_SET) != -1)
|
|
|
|
|
ret = fwrite(ptr, size, count, stream);
|
|
|
|
|
fseeko64(stream, cur_pos, SEEK_SET);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-28 21:12:53 -03:00
|
|
|
static size_t
|
|
|
|
|
viso_convert_utf8(wchar_t *dest, const char *src, int buf_size)
|
|
|
|
|
{
|
|
|
|
|
wchar_t c, *p = dest;
|
|
|
|
|
int next;
|
|
|
|
|
|
|
|
|
|
while (buf_size-- > 0) {
|
|
|
|
|
c = *src;
|
|
|
|
|
if (!c) {
|
|
|
|
|
/* Terminator. */
|
|
|
|
|
*p = 0;
|
|
|
|
|
break;
|
|
|
|
|
} else if (c & 0x80) {
|
|
|
|
|
/* Convert UTF-8 codepoints. */
|
|
|
|
|
next = 0;
|
|
|
|
|
while (c & 0x40) {
|
|
|
|
|
next++;
|
|
|
|
|
c <<= 1;
|
|
|
|
|
}
|
|
|
|
|
c = *src++ & (0x3f >> next);
|
|
|
|
|
while ((next-- > 0) && (buf_size-- > 0))
|
|
|
|
|
c = (c << 6) | (*src++ & 0x3f);
|
|
|
|
|
} else {
|
|
|
|
|
/* Pass through sub-UTF-8 codepoints. */
|
|
|
|
|
src++;
|
|
|
|
|
}
|
|
|
|
|
*p++ = c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p - dest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define VISO_WRITE_STR_FUNC(n, dt, st, cnv) \
|
|
|
|
|
static void \
|
|
|
|
|
n(dt *dest, const st *src, int buf_size, int charset) \
|
|
|
|
|
{ \
|
|
|
|
|
st c; \
|
|
|
|
|
while (buf_size-- > 0) { \
|
|
|
|
|
c = *src++; \
|
|
|
|
|
switch (c) { \
|
|
|
|
|
case 0x00: \
|
|
|
|
|
/* Terminator, apply space padding. */ \
|
|
|
|
|
while (buf_size-- >= 0) \
|
|
|
|
|
*dest++ = cnv(' '); \
|
|
|
|
|
return; \
|
|
|
|
|
\
|
|
|
|
|
case 'A' ... 'Z': \
|
|
|
|
|
case '0' ... '9': \
|
|
|
|
|
case '_': \
|
|
|
|
|
/* Valid on all sets. */ \
|
|
|
|
|
break; \
|
|
|
|
|
\
|
|
|
|
|
case 'a' ... 'z': \
|
|
|
|
|
/* Convert to uppercase on D and A. */ \
|
|
|
|
|
if (charset <= VISO_CHARSET_A) \
|
|
|
|
|
c -= 'a' - 'A'; \
|
|
|
|
|
break; \
|
|
|
|
|
\
|
|
|
|
|
case ' ': \
|
|
|
|
|
case '!': \
|
|
|
|
|
case '"': \
|
|
|
|
|
case '%': \
|
|
|
|
|
case '&': \
|
|
|
|
|
case '(': \
|
|
|
|
|
case ')': \
|
|
|
|
|
case '+': \
|
|
|
|
|
case ',': \
|
|
|
|
|
case '-': \
|
|
|
|
|
case '.': \
|
|
|
|
|
case '<': \
|
|
|
|
|
case '=': \
|
|
|
|
|
case '>': \
|
|
|
|
|
/* Valid for A and filenames but not for D. */ \
|
|
|
|
|
if (charset < VISO_CHARSET_A) \
|
|
|
|
|
c = '_'; \
|
|
|
|
|
break; \
|
|
|
|
|
\
|
|
|
|
|
case '*': \
|
|
|
|
|
case '/': \
|
|
|
|
|
case ':': \
|
|
|
|
|
case ';': \
|
|
|
|
|
case '?': \
|
|
|
|
|
case '\'': \
|
|
|
|
|
/* Valid for A but not for filenames or D. */ \
|
|
|
|
|
if ((charset < VISO_CHARSET_A) || (charset == VISO_CHARSET_FN)) \
|
|
|
|
|
c = '_'; \
|
|
|
|
|
break; \
|
|
|
|
|
\
|
|
|
|
|
case 0x01 ... 0x1f: \
|
|
|
|
|
/* Not valid for A, D or filenames. */ \
|
|
|
|
|
if (charset <= VISO_CHARSET_FN) \
|
|
|
|
|
c = '_'; \
|
|
|
|
|
break; \
|
|
|
|
|
\
|
|
|
|
|
default: \
|
|
|
|
|
/* Not valid for A or D, but valid for filenames. */ \
|
|
|
|
|
if ((charset < VISO_CHARSET_FN) || (c > 0xffff)) \
|
|
|
|
|
c = '_'; \
|
|
|
|
|
break; \
|
|
|
|
|
} \
|
|
|
|
|
*dest++ = cnv(c); \
|
|
|
|
|
} \
|
2022-03-26 22:17:09 -03:00
|
|
|
}
|
|
|
|
|
VISO_WRITE_STR_FUNC(viso_write_string, uint8_t, char, )
|
|
|
|
|
VISO_WRITE_STR_FUNC(viso_write_wstring, uint16_t, wchar_t, cpu_to_be16)
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
viso_get_short_filename(viso_entry_t *dir, char *dest, const char *src)
|
|
|
|
|
{
|
|
|
|
|
/* Get name and extension length. */
|
|
|
|
|
const char *ext_pos = strrchr(src, '.');
|
|
|
|
|
int name_len, ext_len;
|
|
|
|
|
if (ext_pos) {
|
|
|
|
|
name_len = ext_pos - src;
|
|
|
|
|
ext_len = strlen(ext_pos);
|
|
|
|
|
} else {
|
|
|
|
|
name_len = strlen(src);
|
|
|
|
|
ext_len = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy name. */
|
|
|
|
|
int name_copy_len = MIN(8, name_len);
|
|
|
|
|
viso_write_string((uint8_t *) dest, src, name_copy_len, VISO_CHARSET_D);
|
2022-03-28 21:12:53 -03:00
|
|
|
dest[name_copy_len] = '\0';
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
/* Copy extension to temporary buffer. */
|
|
|
|
|
char ext[5] = { 0 };
|
|
|
|
|
int force_tail = (name_len > 8) || (ext_len == 1);
|
|
|
|
|
if (ext_len > 1) {
|
|
|
|
|
ext[0] = '.';
|
2022-03-28 21:12:53 -03:00
|
|
|
if (ext_len > 4) {
|
2022-03-28 22:49:40 -03:00
|
|
|
ext_len = 4;
|
2022-03-26 22:17:09 -03:00
|
|
|
force_tail = 1;
|
2022-03-28 21:12:53 -03:00
|
|
|
}
|
|
|
|
|
viso_write_string((uint8_t *) &ext[1], &ext_pos[1], ext_len - 1, VISO_CHARSET_D);
|
2022-03-26 22:17:09 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check if this filename is unique, and add a tail if required, while also adding the extension. */
|
|
|
|
|
char tail[8];
|
|
|
|
|
for (int i = force_tail; i <= 999999; i++) {
|
|
|
|
|
/* Add tail to the filename if this is not the first run. */
|
|
|
|
|
int tail_len = -1;
|
|
|
|
|
if (i) {
|
|
|
|
|
tail_len = sprintf(tail, "~%d", i);
|
|
|
|
|
strcpy(&dest[MIN(name_copy_len, 8 - tail_len)], tail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add extension to the filename if present. */
|
|
|
|
|
if (ext[0])
|
|
|
|
|
strcat(dest, ext);
|
|
|
|
|
|
|
|
|
|
/* Go through files in this directory to make sure this filename is unique. */
|
|
|
|
|
viso_entry_t *entry = dir->first_child;
|
|
|
|
|
while (entry) {
|
|
|
|
|
/* Flag and stop if this filename was seen. */
|
|
|
|
|
if ((entry->name_short != dest) && !strcmp(dest, entry->name_short)) {
|
|
|
|
|
tail_len = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move on to the next entry, and stop if the end of this directory was reached. */
|
|
|
|
|
entry = entry->next;
|
|
|
|
|
if (entry && (entry->parent != dir))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Stop if this is an unique name. */
|
|
|
|
|
if (tail_len)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-27 16:27:41 -03:00
|
|
|
static int
|
2022-03-28 23:48:22 -03:00
|
|
|
viso_fill_time(uint8_t *data, time_t time, int longform)
|
2022-03-26 22:17:09 -03:00
|
|
|
{
|
2022-03-27 16:27:41 -03:00
|
|
|
uint8_t *p = data;
|
2022-03-28 23:48:22 -03:00
|
|
|
struct tm *time_s = localtime(&time);
|
|
|
|
|
if (!time_s)
|
|
|
|
|
fatal("VISO: localtime(%d) = NULL\n", time);
|
|
|
|
|
|
|
|
|
|
if (longform) {
|
|
|
|
|
p += sprintf((char *) p, "%04d%02d%02d%02d%02d%02d%02d",
|
|
|
|
|
1900 + time_s->tm_year, 1 + time_s->tm_mon, time_s->tm_mday,
|
|
|
|
|
time_s->tm_hour, time_s->tm_min, time_s->tm_sec, 0);
|
|
|
|
|
} else {
|
|
|
|
|
*p++ = time_s->tm_year; /* year since 1900 */
|
|
|
|
|
*p++ = 1 + time_s->tm_mon; /* month */
|
|
|
|
|
*p++ = time_s->tm_mday; /* day */
|
|
|
|
|
*p++ = time_s->tm_hour; /* hour */
|
|
|
|
|
*p++ = time_s->tm_min; /* minute */
|
|
|
|
|
*p++ = time_s->tm_sec; /* second */
|
|
|
|
|
}
|
|
|
|
|
*p++ = tz_offset; /* timezone */
|
2022-03-27 16:27:41 -03:00
|
|
|
|
|
|
|
|
return p - data;
|
|
|
|
|
}
|
2022-03-26 22:17:09 -03:00
|
|
|
|
2022-03-27 16:27:41 -03:00
|
|
|
static int
|
|
|
|
|
viso_fill_dir_record(uint8_t *data, viso_entry_t *entry, int type)
|
|
|
|
|
{
|
|
|
|
|
uint8_t *p = data, *q;
|
2022-03-26 22:17:09 -03:00
|
|
|
|
2022-03-27 16:27:41 -03:00
|
|
|
*p++ = 0; /* size (filled in later) */
|
|
|
|
|
*p++ = 0; /* extended attribute length */
|
|
|
|
|
VISO_SKIP(p, 8); /* sector offset */
|
|
|
|
|
VISO_LBE_32(p, entry->stats.st_size); /* size (filled in later if this is a directory) */
|
2022-03-28 23:48:22 -03:00
|
|
|
p += viso_fill_time(p, entry->stats.st_mtime, 0); /* time */
|
2022-03-26 22:17:09 -03:00
|
|
|
*p++ = S_ISDIR(entry->stats.st_mode) ? 0x02 : 0x00; /* flags */
|
|
|
|
|
|
|
|
|
|
VISO_SKIP(p, 2); /* interleave unit/gap size */
|
|
|
|
|
VISO_LBE_16(p, 1); /* volume sequence number */
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case VISO_DIR_CURRENT:
|
2022-03-27 16:27:41 -03:00
|
|
|
case VISO_DIR_CURRENT_ROOT:
|
2022-03-26 22:17:09 -03:00
|
|
|
case VISO_DIR_PARENT:
|
2022-03-27 16:27:41 -03:00
|
|
|
*p++ = 1; /* file ID length */
|
|
|
|
|
*p++ = (type == VISO_DIR_PARENT) ? 1 : 0; /* magic value corresponding to . or .. */
|
|
|
|
|
|
|
|
|
|
/* Fill Extension Record for the root directory's . entry. */
|
|
|
|
|
if (type == VISO_DIR_CURRENT_ROOT) {
|
|
|
|
|
*p++ = 'E';
|
|
|
|
|
*p++ = 'R';
|
|
|
|
|
*p++ = 8 + (sizeof(rr_eid) - 1) + (sizeof(rr_edesc) - 1); /* length */
|
|
|
|
|
*p++ = 1; /* version */
|
|
|
|
|
|
|
|
|
|
*p++ = sizeof(rr_eid) - 1; /* ID length */
|
|
|
|
|
*p++ = sizeof(rr_edesc) - 1; /* description length */
|
|
|
|
|
*p++ = 0; /* source length (source is recommended but won't fit here) */
|
|
|
|
|
*p++ = 1; /* extension version */
|
|
|
|
|
|
|
|
|
|
memcpy(p, rr_eid, sizeof(rr_eid) - 1); /* ID */
|
|
|
|
|
p += sizeof(rr_eid) - 1;
|
|
|
|
|
memcpy(p, rr_edesc, sizeof(rr_edesc) - 1); /* description */
|
|
|
|
|
p += sizeof(rr_edesc) - 1;
|
|
|
|
|
|
|
|
|
|
goto pad_susp;
|
|
|
|
|
}
|
2022-03-26 22:17:09 -03:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case VISO_DIR_REGULAR:
|
|
|
|
|
q = p++; /* save location of the file ID length for later */
|
|
|
|
|
|
|
|
|
|
*q = strlen(entry->name_short);
|
|
|
|
|
memcpy(p, entry->name_short, *q); /* file ID */
|
|
|
|
|
p += *q;
|
|
|
|
|
if (!S_ISDIR(entry->stats.st_mode)) {
|
|
|
|
|
memcpy(p, ";1", 2); /* version suffix for files */
|
|
|
|
|
p += 2;
|
|
|
|
|
*q += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!((*q) & 1)) /* padding for even file ID lengths */
|
|
|
|
|
*p++ = 0;
|
|
|
|
|
|
|
|
|
|
*p++ = 'R'; /* RR = present Rock Ridge entries (only documented by RRIP revision 1.09!) */
|
|
|
|
|
*p++ = 'R';
|
|
|
|
|
*p++ = 5; /* length */
|
|
|
|
|
*p++ = 1; /* version */
|
|
|
|
|
|
|
|
|
|
q = p++; /* save location of Rock Ridge flags for later */
|
|
|
|
|
|
2022-03-28 22:49:40 -03:00
|
|
|
#ifndef _WIN32 /* attributes reported by MinGW don't really make sense because it's Windows */
|
2022-03-26 22:17:09 -03:00
|
|
|
*q |= 0x01; /* PX = POSIX attributes */
|
|
|
|
|
*p++ = 'P';
|
|
|
|
|
*p++ = 'X';
|
|
|
|
|
*p++ = 36; /* length */
|
|
|
|
|
*p++ = 1; /* version */
|
|
|
|
|
|
|
|
|
|
VISO_LBE_32(p, entry->stats.st_mode); /* mode */
|
|
|
|
|
VISO_LBE_32(p, entry->stats.st_nlink); /* number of links */
|
|
|
|
|
VISO_LBE_32(p, entry->stats.st_uid); /* owner UID */
|
|
|
|
|
VISO_LBE_32(p, entry->stats.st_gid); /* owner GID */
|
|
|
|
|
|
2022-03-28 22:49:40 -03:00
|
|
|
# if defined(S_ISCHR) || defined(S_ISBLK)
|
|
|
|
|
# if defined(S_ISCHR) && defined(S_ISBLK)
|
2022-03-26 22:17:09 -03:00
|
|
|
if (S_ISCHR(entry->stats.st_mode) || S_ISBLK(entry->stats.st_mode))
|
2022-03-28 22:49:40 -03:00
|
|
|
# elif defined(S_ISCHR)
|
2022-03-26 22:17:09 -03:00
|
|
|
if (S_ISCHR(entry->stats.st_mode))
|
2022-03-28 22:49:40 -03:00
|
|
|
# else
|
2022-03-26 22:17:09 -03:00
|
|
|
if (S_ISBLK(entry->stats.st_mode))
|
2022-03-28 22:49:40 -03:00
|
|
|
# endif
|
2022-03-26 22:17:09 -03:00
|
|
|
{
|
|
|
|
|
*q |= 0x02; /* PN = POSIX device */
|
|
|
|
|
*p++ = 'P';
|
|
|
|
|
*p++ = 'N';
|
|
|
|
|
*p++ = 20; /* length */
|
|
|
|
|
*p++ = 1; /* version */
|
|
|
|
|
|
2022-03-28 22:49:40 -03:00
|
|
|
uint64_t dev = entry->stats.st_rdev; /* avoid warning if <= 32 bits */
|
|
|
|
|
VISO_LBE_32(p, dev >> 32); /* device number (high 32 bits) */
|
|
|
|
|
VISO_LBE_32(p, dev); /* device number (low 32 bits) */
|
2022-03-26 22:17:09 -03:00
|
|
|
}
|
2022-03-28 22:49:40 -03:00
|
|
|
# endif
|
2022-03-26 22:17:09 -03:00
|
|
|
#endif
|
2022-03-28 23:48:22 -03:00
|
|
|
if (VISO_TIME_VALID(entry->stats.st_mtime) || VISO_TIME_VALID(entry->stats.st_atime) || VISO_TIME_VALID(entry->stats.st_ctime)) {
|
2022-03-26 22:17:09 -03:00
|
|
|
*q |= 0x80; /* TF = timestamps */
|
|
|
|
|
*p++ = 'T';
|
|
|
|
|
*p++ = 'F';
|
2022-03-29 00:10:21 -03:00
|
|
|
*p++ = 5 + (7 * (VISO_TIME_VALID(entry->stats.st_mtime) + /* length: modify */
|
2022-03-29 00:07:08 -03:00
|
|
|
VISO_TIME_VALID(entry->stats.st_atime) + /* + access */
|
|
|
|
|
VISO_TIME_VALID(entry->stats.st_ctime))); /* + attributes */
|
|
|
|
|
*p++ = 1; /* version */
|
2022-03-27 16:27:41 -03:00
|
|
|
|
2022-03-28 23:48:22 -03:00
|
|
|
*p++ = (VISO_TIME_VALID(entry->stats.st_mtime) << 1) | /* flags: modify */
|
2022-03-29 00:07:08 -03:00
|
|
|
(VISO_TIME_VALID(entry->stats.st_atime) << 2) | /* + access */
|
|
|
|
|
(VISO_TIME_VALID(entry->stats.st_ctime) << 3); /* + attributes */
|
2022-03-28 23:48:22 -03:00
|
|
|
if (VISO_TIME_VALID(entry->stats.st_mtime))
|
|
|
|
|
p += viso_fill_time(p, entry->stats.st_mtime, 0); /* modify */
|
|
|
|
|
if (VISO_TIME_VALID(entry->stats.st_atime))
|
|
|
|
|
p += viso_fill_time(p, entry->stats.st_atime, 0); /* access */
|
|
|
|
|
if (VISO_TIME_VALID(entry->stats.st_ctime))
|
|
|
|
|
p += viso_fill_time(p, entry->stats.st_ctime, 0); /* attributes */
|
2022-03-27 16:27:41 -03:00
|
|
|
}
|
2022-03-26 22:17:09 -03:00
|
|
|
|
2022-03-28 22:49:40 -03:00
|
|
|
/* Trim Rock Ridge name to fit available space. */
|
2022-03-27 16:27:41 -03:00
|
|
|
int max_len = 254 - (p - data) - 5;
|
|
|
|
|
if (entry->name_rr_len > max_len) {
|
|
|
|
|
/* Relocate extension if this is a file whose name exceeds the maximum length. */
|
|
|
|
|
if (!S_ISDIR(entry->stats.st_mode)) {
|
|
|
|
|
char *ext = strrchr(entry->name_rr, '.');
|
|
|
|
|
if (ext) {
|
|
|
|
|
entry->name_rr_len = strlen(ext);
|
|
|
|
|
memmove(entry->name_rr + (max_len - entry->name_rr_len), ext, entry->name_rr_len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
entry->name_rr_len = max_len;
|
2022-03-26 22:17:09 -03:00
|
|
|
}
|
|
|
|
|
|
2022-03-27 16:27:41 -03:00
|
|
|
*q |= 0x08; /* NM = alternate name */
|
|
|
|
|
*p++ = 'N';
|
|
|
|
|
*p++ = 'M';
|
|
|
|
|
*p++ = 5 + entry->name_rr_len; /* length */
|
|
|
|
|
*p++ = 1; /* version */
|
|
|
|
|
|
|
|
|
|
*p++ = 0; /* flags */
|
|
|
|
|
memcpy(p, entry->name_rr, entry->name_rr_len); /* name */
|
|
|
|
|
p += entry->name_rr_len;
|
|
|
|
|
pad_susp:
|
|
|
|
|
if ((p - data) & 1) /* padding for odd SUSP section lengths */
|
2022-03-26 22:17:09 -03:00
|
|
|
*p++ = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case VISO_DIR_JOLIET:
|
|
|
|
|
q = p++; /* save location of the file ID length for later */
|
|
|
|
|
|
2022-03-27 00:27:51 -03:00
|
|
|
*q = entry->name_joliet_len * sizeof(entry->name_joliet[0]);
|
|
|
|
|
memcpy(p, entry->name_joliet, *q); /* file ID */
|
|
|
|
|
p += *q;
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
if (!((*q) & 1)) /* padding for even file ID lengths */
|
|
|
|
|
*p++ = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-27 16:27:41 -03:00
|
|
|
if ((p - data) > 255)
|
|
|
|
|
fatal("VISO: Directory record overflow (%d) on entry %08X\n", p - data, entry);
|
|
|
|
|
|
2022-03-26 22:17:09 -03:00
|
|
|
data[0] = p - data; /* length */
|
2022-03-27 16:27:41 -03:00
|
|
|
return data[0];
|
2022-03-26 22:17:09 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
viso_read(void *p, uint8_t *buffer, uint64_t seek, size_t count)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf = (track_file_t *) p;
|
|
|
|
|
viso_t *viso = (viso_t *) tf->priv;
|
|
|
|
|
|
|
|
|
|
/* Handle reads in a sector by sector basis. */
|
|
|
|
|
while (count > 0) {
|
|
|
|
|
/* Determine the current sector, offset and remainder. */
|
|
|
|
|
uint32_t sector = seek / viso->sector_size,
|
|
|
|
|
sector_offset = seek % viso->sector_size,
|
|
|
|
|
sector_remain = MIN(count, viso->sector_size - sector_offset);
|
|
|
|
|
|
|
|
|
|
/* Handle sector. */
|
|
|
|
|
if (sector < viso->metadata_sectors) {
|
|
|
|
|
/* Copy metadata. */
|
|
|
|
|
memcpy(buffer, viso->metadata + seek, sector_remain);
|
|
|
|
|
} else {
|
|
|
|
|
size_t read = 0;
|
|
|
|
|
|
|
|
|
|
/* Get the file entry corresponding to this sector. */
|
|
|
|
|
viso_entry_t *entry = viso->entry_map[sector - viso->metadata_sectors];
|
|
|
|
|
if (entry) {
|
|
|
|
|
/* Open file if it's not already open. */
|
|
|
|
|
if (!entry->file) {
|
|
|
|
|
/* Close any existing FIFO entry's file. */
|
|
|
|
|
viso_entry_t *other_entry = viso->file_fifo[viso->file_fifo_pos];
|
|
|
|
|
if (other_entry && other_entry->file) {
|
2022-03-27 00:49:07 -03:00
|
|
|
cdrom_image_viso_log("VISO: Closing [%s]", other_entry->path);
|
2022-03-26 22:17:09 -03:00
|
|
|
fclose(other_entry->file);
|
|
|
|
|
other_entry->file = NULL;
|
2022-03-27 00:49:07 -03:00
|
|
|
cdrom_image_viso_log("\n");
|
2022-03-26 22:17:09 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Open file. */
|
|
|
|
|
cdrom_image_viso_log("VISO: Opening [%s]", entry->path);
|
|
|
|
|
if ((entry->file = fopen(entry->path, "rb"))) {
|
|
|
|
|
cdrom_image_viso_log("\n");
|
|
|
|
|
|
|
|
|
|
/* Add this entry to the FIFO. */
|
|
|
|
|
viso->file_fifo[viso->file_fifo_pos++] = entry;
|
|
|
|
|
viso->file_fifo_pos &= (sizeof(viso->file_fifo) / sizeof(viso->file_fifo[0])) - 1;
|
|
|
|
|
} else {
|
|
|
|
|
cdrom_image_viso_log(" => failed\n");
|
|
|
|
|
|
|
|
|
|
/* Clear any existing FIFO entry. */
|
|
|
|
|
viso->file_fifo[viso->file_fifo_pos] = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read data. */
|
|
|
|
|
if (entry->file && (fseeko64(entry->file, seek - entry->data_offset, SEEK_SET) != -1))
|
|
|
|
|
read = fread(buffer, 1, sector_remain, entry->file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fill remainder with 00 bytes if needed. */
|
|
|
|
|
if (read < sector_remain)
|
|
|
|
|
memset(buffer + read, 0x00, sector_remain - read);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move on to the next sector. */
|
|
|
|
|
buffer += sector_remain;
|
|
|
|
|
seek += sector_remain;
|
|
|
|
|
count -= sector_remain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t
|
|
|
|
|
viso_get_length(void *p)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf = (track_file_t *) p;
|
|
|
|
|
viso_t *viso = (viso_t *) tf->priv;
|
|
|
|
|
return ((uint64_t) viso->all_sectors) * viso->sector_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
viso_close(void *p)
|
|
|
|
|
{
|
|
|
|
|
track_file_t *tf = (track_file_t *) p;
|
|
|
|
|
viso_t *viso = (viso_t *) tf->priv;
|
|
|
|
|
|
|
|
|
|
if (viso == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
cdrom_image_viso_log("VISO: close()\n");
|
|
|
|
|
|
|
|
|
|
/* De-allocate everything. */
|
|
|
|
|
if (tf->file)
|
|
|
|
|
fclose(tf->file);
|
|
|
|
|
|
|
|
|
|
viso_entry_t *entry = &viso->root_dir, *next_entry;
|
|
|
|
|
while (entry) {
|
|
|
|
|
if (entry->path)
|
|
|
|
|
free(entry->path);
|
|
|
|
|
if (entry->file)
|
|
|
|
|
fclose(entry->file);
|
|
|
|
|
next_entry = entry->next;
|
|
|
|
|
if (entry != &viso->root_dir)
|
|
|
|
|
free(entry);
|
|
|
|
|
entry = next_entry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (viso->metadata)
|
|
|
|
|
free(viso->metadata);
|
|
|
|
|
if (viso->entry_map)
|
|
|
|
|
free(viso->entry_map);
|
|
|
|
|
|
|
|
|
|
free(viso);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
track_file_t *
|
|
|
|
|
viso_init(const char *dirname, int *error)
|
|
|
|
|
{
|
|
|
|
|
cdrom_image_viso_log("VISO: init()\n");
|
|
|
|
|
|
|
|
|
|
/* Initialize our data structure. */
|
|
|
|
|
viso_t *viso = (viso_t *) calloc(1, sizeof(viso_t));
|
|
|
|
|
uint8_t *data = NULL, *p;
|
|
|
|
|
wchar_t *wtemp = NULL;
|
|
|
|
|
*error = 1;
|
|
|
|
|
if (viso == NULL)
|
|
|
|
|
goto end;
|
|
|
|
|
viso->sector_size = VISO_SECTOR_SIZE;
|
|
|
|
|
|
|
|
|
|
/* Prepare temporary data buffers. */
|
|
|
|
|
data = calloc(2, viso->sector_size);
|
|
|
|
|
int wtemp_len = MIN(64, sizeof(viso->root_dir.name_joliet) / sizeof(viso->root_dir.name_joliet[0])) + 1;
|
|
|
|
|
wtemp = malloc(wtemp_len * sizeof(wchar_t));
|
|
|
|
|
if (!data || !wtemp)
|
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
|
|
/* Open temporary file. */
|
|
|
|
|
#ifdef ENABLE_CDROM_IMAGE_VISO_LOG
|
|
|
|
|
strcpy(viso->tf.fn, "viso-debug.iso");
|
|
|
|
|
#else
|
|
|
|
|
plat_tempfile(viso->tf.fn, "viso", ".tmp");
|
|
|
|
|
#endif
|
|
|
|
|
viso->tf.file = plat_fopen64(nvr_path(viso->tf.fn), "w+b");
|
|
|
|
|
if (!viso->tf.file)
|
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
|
|
/* Set up directory traversal. */
|
|
|
|
|
cdrom_image_viso_log("VISO: Traversing directories:\n");
|
|
|
|
|
viso_entry_t *dir = &viso->root_dir, *last_dir = dir, *last_entry = dir;
|
|
|
|
|
struct dirent *readdir_entry;
|
|
|
|
|
int max_len, len, name_len;
|
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
|
|
/* Fill root directory entry. */
|
|
|
|
|
dir->path = (char *) malloc(strlen(dirname) + 1);
|
|
|
|
|
if (!dir->path)
|
|
|
|
|
goto end;
|
|
|
|
|
strcpy(dir->path, dirname);
|
|
|
|
|
stat(dirname, &dir->stats);
|
|
|
|
|
if (!S_ISDIR(dir->stats.st_mode))
|
|
|
|
|
goto end;
|
2022-03-31 12:54:46 -03:00
|
|
|
dir->parent = dir; /* for the root's path table and .. entries */
|
2022-03-26 22:17:09 -03:00
|
|
|
cdrom_image_viso_log("[%08X] %s => [root]\n", dir, dir->path);
|
|
|
|
|
|
|
|
|
|
/* Traverse directories, starting with the root. */
|
|
|
|
|
while (dir) {
|
|
|
|
|
/* Open directory for listing. */
|
|
|
|
|
DIR *dirp = opendir(dir->path);
|
|
|
|
|
if (!dirp)
|
|
|
|
|
goto next_dir;
|
|
|
|
|
|
|
|
|
|
/* Add . and .. pseudo-directories. */
|
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
|
last_entry->next = (viso_entry_t *) calloc(1, sizeof(viso_entry_t));
|
|
|
|
|
if (!last_entry->next)
|
|
|
|
|
goto end;
|
|
|
|
|
last_entry = last_entry->next;
|
|
|
|
|
last_entry->parent = dir;
|
|
|
|
|
if (!i)
|
|
|
|
|
dir->first_child = last_entry;
|
|
|
|
|
|
|
|
|
|
/* Stat the current directory or parent directory. */
|
|
|
|
|
stat(i ? dir->parent->path : dir->path, &last_entry->stats);
|
|
|
|
|
|
|
|
|
|
/* Set short and long filenames. */
|
|
|
|
|
strcpy(last_entry->name_short, i ? ".." : ".");
|
|
|
|
|
strcpy(last_entry->name_rr, i ? ".." : ".");
|
|
|
|
|
wcscpy(last_entry->name_joliet, i ? L".." : L".");
|
|
|
|
|
|
|
|
|
|
cdrom_image_viso_log("[%08X] %s => %s\n", last_entry, dir->path, last_entry->name_short);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Iterate through this directory's children. */
|
|
|
|
|
size_t dir_path_len = strlen(dir->path);
|
|
|
|
|
while ((readdir_entry = readdir(dirp))) {
|
|
|
|
|
/* Ignore . and .. pseudo-directories. */
|
|
|
|
|
if (readdir_entry->d_name[0] == '.' && (readdir_entry->d_name[1] == '\0' || (readdir_entry->d_name[1] == '.' && readdir_entry->d_name[2] == '\0')))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Save full file path. */
|
|
|
|
|
name_len = strlen(readdir_entry->d_name);
|
|
|
|
|
path = (char *) malloc(dir_path_len + name_len + 2);
|
|
|
|
|
if (!path)
|
|
|
|
|
goto end;
|
|
|
|
|
strcpy(path, dir->path);
|
|
|
|
|
plat_path_slash(path);
|
|
|
|
|
strcat(path, readdir_entry->d_name);
|
|
|
|
|
|
|
|
|
|
/* Add and fill entry. */
|
|
|
|
|
last_entry->next = (viso_entry_t *) calloc(1, sizeof(viso_entry_t));
|
|
|
|
|
if (!last_entry->next) {
|
|
|
|
|
free(path);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
last_entry = last_entry->next;
|
|
|
|
|
last_entry->path = path;
|
|
|
|
|
last_entry->parent = dir;
|
|
|
|
|
|
|
|
|
|
/* Stat this child. */
|
|
|
|
|
if (stat(path, &last_entry->stats) != 0) {
|
|
|
|
|
/* Use a blank structure if stat failed. */
|
|
|
|
|
memset(&last_entry->stats, 0x00, sizeof(struct stat));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle file size. */
|
|
|
|
|
if (!S_ISDIR(last_entry->stats.st_mode)) {
|
|
|
|
|
/* Limit to 4 GB - 1 byte. */
|
|
|
|
|
if (last_entry->stats.st_size > ((uint32_t) -1))
|
|
|
|
|
last_entry->stats.st_size = (uint32_t) -1;
|
|
|
|
|
|
|
|
|
|
/* Increase entry map size. */
|
|
|
|
|
viso->entry_map_size += last_entry->stats.st_size / viso->sector_size;
|
|
|
|
|
if (last_entry->stats.st_size % viso->sector_size)
|
|
|
|
|
viso->entry_map_size++; /* round up to the next sector */
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-27 13:37:06 -03:00
|
|
|
/* Detect El Torito boot code file and set it accordingly. */
|
2022-03-27 14:37:40 -03:00
|
|
|
if ((dir == &viso->root_dir) && !strnicmp(readdir_entry->d_name, "eltorito.", 9) && (!stricmp(readdir_entry->d_name + 9, "com") || !stricmp(readdir_entry->d_name + 9, "img")))
|
2022-03-27 13:37:06 -03:00
|
|
|
viso->eltorito_entry = last_entry;
|
|
|
|
|
|
2022-03-26 22:17:09 -03:00
|
|
|
/* Set short filename. */
|
|
|
|
|
if (viso_get_short_filename(dir, last_entry->name_short, readdir_entry->d_name))
|
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
|
|
/* Set Rock Ridge long filename. */
|
|
|
|
|
len = MIN(name_len, sizeof(last_entry->name_rr) - 1);
|
|
|
|
|
viso_write_string((uint8_t *) last_entry->name_rr, readdir_entry->d_name, len, VISO_CHARSET_FN);
|
|
|
|
|
last_entry->name_rr[len] = '\0';
|
2022-03-27 16:27:41 -03:00
|
|
|
last_entry->name_rr_len = len;
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
/* Set Joliet long filename. */
|
|
|
|
|
if (wtemp_len < (name_len + 1)) { /* grow wchar buffer if needed */
|
|
|
|
|
wtemp_len = name_len + 1;
|
|
|
|
|
wtemp = realloc(wtemp, wtemp_len * sizeof(wchar_t));
|
|
|
|
|
}
|
|
|
|
|
max_len = (sizeof(last_entry->name_joliet) / sizeof(last_entry->name_joliet[0])) - 1;
|
2022-03-28 21:12:53 -03:00
|
|
|
len = viso_convert_utf8(wtemp, readdir_entry->d_name, wtemp_len);
|
2022-03-26 22:17:09 -03:00
|
|
|
if (len > max_len) {
|
|
|
|
|
/* Relocate extension if this is a file whose name exceeds the maximum length. */
|
|
|
|
|
if (!S_ISDIR(last_entry->stats.st_mode)) {
|
|
|
|
|
wchar_t *wext = wcsrchr(wtemp, L'.');
|
|
|
|
|
if (wext) {
|
|
|
|
|
len = wcslen(wext);
|
|
|
|
|
memmove(wtemp + (max_len - len), wext, len * sizeof(wchar_t));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
len = max_len;
|
|
|
|
|
}
|
|
|
|
|
viso_write_wstring(last_entry->name_joliet, wtemp, len, VISO_CHARSET_FN);
|
|
|
|
|
last_entry->name_joliet[len] = '\0';
|
2022-03-27 00:27:51 -03:00
|
|
|
last_entry->name_joliet_len = len;
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
cdrom_image_viso_log("[%08X] %s => [%-12s] %s\n", last_entry, dir->path, last_entry->name_short, last_entry->name_rr);
|
|
|
|
|
|
|
|
|
|
/* If this is a directory, add it to the traversal list. */
|
|
|
|
|
if (S_ISDIR(last_entry->stats.st_mode)) {
|
|
|
|
|
last_dir->next_dir = last_entry;
|
|
|
|
|
last_dir = last_entry;
|
|
|
|
|
last_dir->first_child = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_dir:
|
|
|
|
|
/* Move on to the next directory. */
|
|
|
|
|
dir = dir->next_dir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write 16 blank sectors. */
|
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
|
|
|
fwrite(data, viso->sector_size, 1, viso->tf.file);
|
|
|
|
|
|
2022-03-28 23:48:22 -03:00
|
|
|
/* Get current time for the volume descriptors, and calculate
|
|
|
|
|
the timezone offset for descriptors and file times to use. */
|
2022-03-29 12:03:28 -03:00
|
|
|
tzset();
|
2022-03-28 23:48:22 -03:00
|
|
|
time_t now = time(NULL);
|
|
|
|
|
tz_offset = (now - mktime(gmtime(&now))) / (3600 / 4);
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
/* Get root directory basename for the volume ID. */
|
|
|
|
|
char *basename = plat_get_basename(dirname);
|
|
|
|
|
if (!basename || (basename[0] == '\0'))
|
|
|
|
|
basename = EMU_NAME;
|
|
|
|
|
|
|
|
|
|
/* Write volume descriptors. */
|
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
|
/* Fill volume descriptor. */
|
|
|
|
|
p = data;
|
|
|
|
|
*p++ = 1 + i; /* type */
|
|
|
|
|
memcpy(p, "CD001", 5); /* standard ID */
|
|
|
|
|
p += 5;
|
|
|
|
|
*p++ = 1; /* version */
|
|
|
|
|
*p++ = 0; /* unused */
|
|
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
|
viso_write_wstring((uint16_t *) p, EMU_NAME_W, 16, VISO_CHARSET_A); /* system ID */
|
|
|
|
|
p += 32;
|
|
|
|
|
mbstowcs(wtemp, basename, 16);
|
|
|
|
|
viso_write_wstring((uint16_t *) p, wtemp, 16, VISO_CHARSET_D); /* volume ID */
|
|
|
|
|
p += 32;
|
|
|
|
|
} else {
|
|
|
|
|
viso_write_string(p, EMU_NAME, 32, VISO_CHARSET_A); /* system ID */
|
|
|
|
|
p += 32;
|
|
|
|
|
viso_write_string(p, basename, 32, VISO_CHARSET_D); /* volume ID */
|
|
|
|
|
p += 32;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VISO_SKIP(p, 8); /* unused */
|
|
|
|
|
|
|
|
|
|
viso->vol_size_offsets[i] = ftello64(viso->tf.file) + (p - data);
|
|
|
|
|
VISO_LBE_32(p, 0); /* volume space size (filled in later) */
|
|
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
|
*p++ = 0x25; /* escape sequence (indicates our Joliet names are UCS-2 Level 3) */
|
|
|
|
|
*p++ = 0x2f;
|
|
|
|
|
*p++ = 0x45;
|
|
|
|
|
VISO_SKIP(p, 32 - 3); /* unused */
|
|
|
|
|
} else {
|
|
|
|
|
VISO_SKIP(p, 32); /* unused */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VISO_LBE_16(p, 1); /* volume set size */
|
|
|
|
|
VISO_LBE_16(p, 1); /* volume sequence number */
|
|
|
|
|
VISO_LBE_16(p, viso->sector_size); /* logical block size */
|
|
|
|
|
|
|
|
|
|
/* Path table metadata is filled in later. */
|
|
|
|
|
viso->pt_meta_offsets[i] = ftello64(viso->tf.file) + (p - data);
|
|
|
|
|
VISO_LBE_32(p, 0); /* path table size */
|
|
|
|
|
VISO_LBE_32(p, 0); /* little endian path table and optional path table sector (VISO_LBE_32 is a shortcut to set both) */
|
|
|
|
|
VISO_LBE_32(p, 0); /* big endian path table and optional path table sector (VISO_LBE_32 is a shortcut to set both) */
|
|
|
|
|
|
|
|
|
|
viso->root_dir.dr_offsets[i] = ftello64(viso->tf.file) + (p - data);
|
2022-03-27 16:27:41 -03:00
|
|
|
p += viso_fill_dir_record(p, &viso->root_dir, VISO_DIR_CURRENT); /* root directory */
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
|
viso_write_wstring((uint16_t *) p, L"", 64, VISO_CHARSET_D); /* volume set ID */
|
|
|
|
|
p += 128;
|
|
|
|
|
viso_write_wstring((uint16_t *) p, L"", 64, VISO_CHARSET_A); /* publisher ID */
|
|
|
|
|
p += 128;
|
|
|
|
|
viso_write_wstring((uint16_t *) p, L"", 64, VISO_CHARSET_A); /* data preparer ID */
|
|
|
|
|
p += 128;
|
|
|
|
|
swprintf(wtemp, 64, L"%ls %ls VIRTUAL ISO", EMU_NAME_W, EMU_VERSION_W);
|
|
|
|
|
viso_write_wstring((uint16_t *) p, wtemp, 64, VISO_CHARSET_A); /* application ID */
|
|
|
|
|
p += 128;
|
|
|
|
|
viso_write_wstring((uint16_t *) p, L"", 18, VISO_CHARSET_D); /* copyright file ID */
|
|
|
|
|
p += 37;
|
|
|
|
|
viso_write_wstring((uint16_t *) p, L"", 18, VISO_CHARSET_D); /* abstract file ID */
|
|
|
|
|
p += 37;
|
|
|
|
|
viso_write_wstring((uint16_t *) p, L"", 18, VISO_CHARSET_D); /* bibliography file ID */
|
|
|
|
|
p += 37;
|
|
|
|
|
} else {
|
|
|
|
|
viso_write_string(p, "", 128, VISO_CHARSET_D); /* volume set ID */
|
|
|
|
|
p += 128;
|
|
|
|
|
viso_write_string(p, "", 128, VISO_CHARSET_A); /* publisher ID */
|
|
|
|
|
p += 128;
|
|
|
|
|
viso_write_string(p, "", 128, VISO_CHARSET_A); /* data preparer ID */
|
|
|
|
|
p += 128;
|
|
|
|
|
snprintf((char *) p, 128, "%s %s VIRTUAL ISO", EMU_NAME, EMU_VERSION);
|
|
|
|
|
viso_write_string(p, (char *) p, 128, VISO_CHARSET_A); /* application ID */
|
|
|
|
|
p += 128;
|
|
|
|
|
viso_write_string(p, "", 37, VISO_CHARSET_D); /* copyright file ID */
|
|
|
|
|
p += 37;
|
|
|
|
|
viso_write_string(p, "", 37, VISO_CHARSET_D); /* abstract file ID */
|
|
|
|
|
p += 37;
|
|
|
|
|
viso_write_string(p, "", 37, VISO_CHARSET_D); /* bibliography file ID */
|
|
|
|
|
p += 37;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-28 23:48:22 -03:00
|
|
|
len = viso_fill_time(p, now, 1); /* volume created */
|
|
|
|
|
memcpy(p + len, p, len); /* volume modified */
|
|
|
|
|
p += len * 2;
|
|
|
|
|
VISO_SKIP(p, len * 2); /* volume expires/effective */
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
*p++ = 1; /* file structure version */
|
|
|
|
|
*p++ = 0; /* unused */
|
|
|
|
|
|
|
|
|
|
/* Blank the rest of the working sector. */
|
|
|
|
|
memset(p, 0x00, viso->sector_size - (p - data));
|
|
|
|
|
|
|
|
|
|
/* Write volume descriptor. */
|
|
|
|
|
fwrite(data, viso->sector_size, 1, viso->tf.file);
|
2022-03-27 13:37:06 -03:00
|
|
|
|
|
|
|
|
/* 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) {
|
2022-03-27 16:37:38 -03:00
|
|
|
cdrom_image_viso_log("VISO: Writing El Torito boot descriptor for entry [%08X]\n", viso->eltorito_entry);
|
|
|
|
|
|
2022-03-27 13:37:06 -03:00
|
|
|
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);
|
|
|
|
|
}
|
2022-03-26 22:17:09 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fill terminator. */
|
|
|
|
|
p = data;
|
|
|
|
|
*p++ = 0xff;
|
|
|
|
|
memcpy(p, "CD001", 5);
|
|
|
|
|
p += 5;
|
|
|
|
|
*p++ = 0x01;
|
|
|
|
|
|
|
|
|
|
/* Blank the rest of the working sector. */
|
|
|
|
|
memset(p, 0x00, viso->sector_size - (p - data));
|
|
|
|
|
|
|
|
|
|
/* Write terminator. */
|
|
|
|
|
fwrite(data, viso->sector_size, 1, viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* We start seeing a pattern of padding to even sectors here.
|
|
|
|
|
mkisofs does this, presumably for a very good reason... */
|
|
|
|
|
int 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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-27 13:37:06 -03:00
|
|
|
/* Handle El Torito boot catalog. */
|
|
|
|
|
if (viso->eltorito_entry) {
|
|
|
|
|
/* Write a pointer to this boot catalog to the boot descriptor. */
|
2022-03-31 12:54:46 -03:00
|
|
|
*((uint32_t *) data) = cpu_to_le32(ftello64(viso->tf.file) / viso->sector_size);
|
2022-03-27 13:37:06 -03:00
|
|
|
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 */
|
2022-03-27 14:37:40 -03:00
|
|
|
*p++ = 0x00; /* checksum */
|
2022-03-27 13:37:06 -03:00
|
|
|
*p++ = 0x00;
|
|
|
|
|
*p++ = 0x55; /* key bytes */
|
|
|
|
|
*p++ = 0xaa;
|
|
|
|
|
|
|
|
|
|
/* Calculate checksum. */
|
|
|
|
|
uint16_t eltorito_checksum = 0;
|
|
|
|
|
for (int i = 0; i < (p - data); i += 2)
|
2022-03-31 12:54:46 -03:00
|
|
|
eltorito_checksum -= le16_to_cpu(*((uint16_t *) &data[i]));
|
|
|
|
|
*((uint16_t *) &data[28]) = cpu_to_le16(eltorito_checksum);
|
2022-03-27 13:37:06 -03:00
|
|
|
|
|
|
|
|
/* Now fill the default boot entry. */
|
|
|
|
|
*p++ = 0x88; /* bootable flag */
|
|
|
|
|
|
2022-03-27 16:37:38 -03:00
|
|
|
if (viso->eltorito_entry->name_short[9] == 'C') { /* boot media type: non-emulation */
|
2022-03-27 13:37:06 -03:00
|
|
|
*p++ = 0x00;
|
2022-03-27 16:37:38 -03:00
|
|
|
} else { /* boot media type: emulation */
|
2022-03-27 13:37:06 -03:00
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-26 22:17:09 -03:00
|
|
|
/* Write each path table. */
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
cdrom_image_viso_log("VISO: Generating path table #%d:\n", i);
|
|
|
|
|
|
|
|
|
|
/* Save this path table's start offset. */
|
|
|
|
|
uint64_t pt_start = ftello64(viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* Write this table's sector offset to the corresponding volume descriptor. */
|
|
|
|
|
uint32_t pt_temp = pt_start / viso->sector_size;
|
2022-03-31 12:54:46 -03:00
|
|
|
*((uint32_t *) data) = (i & 1) ? cpu_to_be32(pt_temp) : cpu_to_le32(pt_temp);
|
2022-03-26 22:17:09 -03:00
|
|
|
viso_pwrite(data, viso->pt_meta_offsets[i >> 1] + 8 + (8 * (i & 1)), 4, 1, viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* Go through directories. */
|
|
|
|
|
dir = &viso->root_dir;
|
|
|
|
|
uint16_t pt_idx = 1;
|
|
|
|
|
while (dir) {
|
|
|
|
|
/* Ignore . and .. pseudo-directories. */
|
|
|
|
|
if (dir->name_short[0] == '.' && (dir->name_short[1] == '\0' || (dir->name_short[1] == '.' && dir->name_short[2] == '\0'))) {
|
|
|
|
|
dir = dir->next_dir;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cdrom_image_viso_log("[%08X] %s => %s\n", dir, dir->path, (i & 2) ? dir->name_rr : dir->name_short);
|
|
|
|
|
|
|
|
|
|
/* Save this directory's path table index and offset. */
|
|
|
|
|
dir->pt_idx = pt_idx;
|
|
|
|
|
dir->pt_offsets[i] = ftello64(viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* Fill path table entry. */
|
|
|
|
|
if (dir == &viso->root_dir) /* directory ID length */
|
|
|
|
|
data[0] = 1;
|
|
|
|
|
else if (i & 2)
|
2022-03-27 00:49:07 -03:00
|
|
|
data[0] = dir->name_joliet_len * sizeof(dir->name_joliet[0]);
|
2022-03-26 22:17:09 -03:00
|
|
|
else
|
|
|
|
|
data[0] = strlen(dir->name_short);
|
|
|
|
|
|
|
|
|
|
data[1] = 0; /* extended attribute length */
|
|
|
|
|
*((uint32_t *) &data[2]) = 0; /* extent location (filled in later) */
|
2022-03-31 12:54:46 -03:00
|
|
|
|
|
|
|
|
*((uint16_t *) &data[6]) = (i & 1) ? cpu_to_be16(dir->parent->pt_idx) : cpu_to_le16(dir->parent->pt_idx); /* parent directory number */
|
2022-03-26 22:17:09 -03:00
|
|
|
|
2022-03-27 00:49:07 -03:00
|
|
|
if (dir == &viso->root_dir) /* directory ID */
|
|
|
|
|
data[8] = 0;
|
|
|
|
|
else if (i & 2)
|
2022-03-26 22:17:09 -03:00
|
|
|
memcpy(&data[8], dir->name_joliet, data[0]);
|
|
|
|
|
else
|
|
|
|
|
memcpy(&data[8], dir->name_short, data[0]);
|
2022-03-27 00:49:07 -03:00
|
|
|
data[8 + data[0]] = 0; /* padding for odd directory ID lengths */
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
/* Write path table entry. */
|
|
|
|
|
fwrite(data, 8 + data[0] + (data[0] & 1), 1, viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* Increment path table index and stop if it overflows. */
|
|
|
|
|
if (++pt_idx == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Move on to the next directory. */
|
|
|
|
|
dir = dir->next_dir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write this table's size to the corresponding volume descriptor. */
|
|
|
|
|
pt_temp = ftello64(viso->tf.file) - pt_start;
|
|
|
|
|
p = data;
|
|
|
|
|
VISO_LBE_32(p, pt_temp);
|
|
|
|
|
viso_pwrite(data, viso->pt_meta_offsets[i >> 1], 8, 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 directory records for each type. */
|
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
|
cdrom_image_viso_log("VISO: Generating directory record set #%d:\n", i);
|
|
|
|
|
|
|
|
|
|
/* Go through directories. */
|
|
|
|
|
dir = &viso->root_dir;
|
|
|
|
|
while (dir) {
|
|
|
|
|
/* Pad to the next sector if required. */
|
|
|
|
|
write = ftello64(viso->tf.file) % viso->sector_size;
|
|
|
|
|
if (write) {
|
|
|
|
|
write = viso->sector_size - write;
|
|
|
|
|
memset(data, 0x00, write);
|
|
|
|
|
fwrite(data, write, 1, viso->tf.file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Save this directory's child record array's start offset. */
|
|
|
|
|
uint64_t dir_start = ftello64(viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* Write this directory's child record array's sector offset to its record... */
|
|
|
|
|
uint32_t dir_temp = dir_start / viso->sector_size;
|
|
|
|
|
p = data;
|
|
|
|
|
VISO_LBE_32(p, dir_temp);
|
|
|
|
|
viso_pwrite(data, dir->dr_offsets[i] + 2, 8, 1, viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* ...and to its path table entries. */
|
|
|
|
|
viso_pwrite(data, dir->pt_offsets[i << 1] + 2, 4, 1, viso->tf.file); /* little endian */
|
|
|
|
|
viso_pwrite(data + 4, dir->pt_offsets[(i << 1) | 1] + 2, 4, 1, viso->tf.file); /* big endian */
|
|
|
|
|
|
2022-03-27 16:33:02 -03:00
|
|
|
if (i) /* clear union if we no longer need path table offsets */
|
|
|
|
|
dir->file = NULL;
|
|
|
|
|
|
2022-03-26 22:17:09 -03:00
|
|
|
/* Go through entries in this directory. */
|
|
|
|
|
viso_entry_t *entry = dir->first_child;
|
2022-03-27 16:27:41 -03:00
|
|
|
int dir_type = (dir == &viso->root_dir) ? VISO_DIR_CURRENT_ROOT : VISO_DIR_CURRENT;
|
2022-03-26 22:17:09 -03:00
|
|
|
while (entry) {
|
2022-03-27 13:37:06 -03:00
|
|
|
/* Skip the El Torito boot code entry if present. */
|
|
|
|
|
if (entry == viso->eltorito_entry)
|
|
|
|
|
goto next_entry;
|
|
|
|
|
|
2022-03-26 22:17:09 -03:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
/* Fill directory record. */
|
2022-03-27 16:27:41 -03:00
|
|
|
viso_fill_dir_record(data, entry, dir_type);
|
2022-03-26 22:17:09 -03:00
|
|
|
|
|
|
|
|
/* Entries cannot cross sector boundaries, so pad to the next sector if needed. */
|
|
|
|
|
write = viso->sector_size - (ftello64(viso->tf.file) % viso->sector_size);
|
|
|
|
|
if (write < data[0]) {
|
|
|
|
|
p = data + (viso->sector_size * 2) - write;
|
|
|
|
|
memset(p, 0x00, write);
|
|
|
|
|
fwrite(p, write, 1, viso->tf.file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write this entry's record's offset. */
|
|
|
|
|
entry->dr_offsets[i] = ftello64(viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* Write data related to the . and .. pseudo-subdirectories,
|
|
|
|
|
while advancing the current directory type. */
|
2022-03-27 16:27:41 -03:00
|
|
|
if (dir_type < VISO_DIR_PARENT) {
|
2022-03-26 22:17:09 -03:00
|
|
|
/* Write a self-referential pointer to this entry. */
|
|
|
|
|
p = data + 2;
|
|
|
|
|
VISO_LBE_32(p, dir_temp);
|
|
|
|
|
|
|
|
|
|
dir_type = VISO_DIR_PARENT;
|
|
|
|
|
} else if (dir_type == VISO_DIR_PARENT) {
|
|
|
|
|
/* Copy the parent directory's offset and size. The root directory's
|
|
|
|
|
parent size is a special, self-referential case handled later. */
|
|
|
|
|
viso_pread(data + 2, dir->parent->dr_offsets[i] + 2, 16, 1, viso->tf.file);
|
|
|
|
|
|
|
|
|
|
dir_type = i ? VISO_DIR_JOLIET : VISO_DIR_REGULAR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write entry. */
|
|
|
|
|
fwrite(data, data[0], 1, viso->tf.file);
|
2022-03-27 13:37:06 -03:00
|
|
|
next_entry:
|
2022-03-26 22:17:09 -03:00
|
|
|
/* Move on to the next entry, and stop if the end of this directory was reached. */
|
|
|
|
|
entry = entry->next;
|
|
|
|
|
if (entry && (entry->parent != dir))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write this directory's child record array's size to its parent and . records. */
|
|
|
|
|
dir_temp = ftello64(viso->tf.file) - dir_start;
|
|
|
|
|
p = data;
|
|
|
|
|
VISO_LBE_32(p, dir_temp);
|
|
|
|
|
viso_pwrite(data, dir->dr_offsets[i] + 10, 8, 1, viso->tf.file);
|
|
|
|
|
viso_pwrite(data, dir->first_child->dr_offsets[i] + 10, 8, 1, viso->tf.file);
|
|
|
|
|
if (dir->parent == dir) /* write size to .. on root directory as well */
|
|
|
|
|
viso_pwrite(data, dir->first_child->next->dr_offsets[i] + 10, 8, 1, viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* Move on to the next directory. */
|
|
|
|
|
dir = dir->next_dir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allocate entry map for sector->file lookups. */
|
2022-03-28 13:08:13 -03:00
|
|
|
cdrom_image_viso_log("VISO: Allocating %d-sector entry map\n", viso->entry_map_size);
|
2022-03-27 18:30:42 -03:00
|
|
|
viso->entry_map = (viso_entry_t **) calloc(viso->entry_map_size, sizeof(viso_entry_t *));
|
|
|
|
|
if (!viso->entry_map)
|
|
|
|
|
goto end;
|
2022-03-26 22:17:09 -03:00
|
|
|
viso->metadata_sectors = ftello64(viso->tf.file) / viso->sector_size;
|
|
|
|
|
viso->all_sectors = viso->metadata_sectors;
|
|
|
|
|
|
2022-03-28 13:08:13 -03:00
|
|
|
/* Go through files, assigning sectors to them. */
|
|
|
|
|
cdrom_image_viso_log("VISO: Assigning sectors to files:\n");
|
2022-03-26 22:17:09 -03:00
|
|
|
viso_entry_t *prev_entry = &viso->root_dir,
|
|
|
|
|
*entry = prev_entry->next,
|
|
|
|
|
**entry_map_p = viso->entry_map;
|
|
|
|
|
while (entry) {
|
|
|
|
|
/* Skip this entry if it corresponds to a directory. */
|
|
|
|
|
if (S_ISDIR(entry->stats.st_mode)) {
|
|
|
|
|
/* Deallocate directory entries to save some memory. */
|
|
|
|
|
prev_entry->next = entry->next;
|
|
|
|
|
free(entry);
|
|
|
|
|
entry = prev_entry->next;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-27 13:37:06 -03:00
|
|
|
/* 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. */
|
2022-03-27 16:37:38 -03:00
|
|
|
if (entry->name_short[9] == 'C') { /* non-emulation */
|
2022-03-27 13:37:06 -03:00
|
|
|
uint32_t boot_size = entry->stats.st_size;
|
|
|
|
|
if (boot_size % 512) /* round up */
|
|
|
|
|
boot_size += 512 - (boot_size % 512);
|
2022-03-31 12:54:46 -03:00
|
|
|
*((uint16_t *) &data[0]) = cpu_to_le16(boot_size / 512);
|
2022-03-27 16:37:38 -03:00
|
|
|
} else { /* emulation */
|
2022-03-31 12:54:46 -03:00
|
|
|
*((uint16_t *) &data[0]) = cpu_to_le16(1);
|
2022-03-27 13:37:06 -03:00
|
|
|
}
|
2022-03-31 12:54:46 -03:00
|
|
|
*((uint32_t *) &data[2]) = cpu_to_le32(viso->all_sectors);
|
2022-03-27 13:37:06 -03:00
|
|
|
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);
|
|
|
|
|
}
|
2022-03-26 22:17:09 -03:00
|
|
|
|
2022-03-27 00:49:07 -03:00
|
|
|
/* Save this file's starting offset. This overwrites dr_offsets in the union. */
|
2022-03-27 00:27:51 -03:00
|
|
|
entry->data_offset = ((uint64_t) viso->all_sectors) * viso->sector_size;
|
|
|
|
|
|
2022-03-26 22:17:09 -03:00
|
|
|
/* Determine how many sectors this file will take. */
|
|
|
|
|
uint32_t size = entry->stats.st_size / viso->sector_size;
|
|
|
|
|
if (entry->stats.st_size % viso->sector_size)
|
|
|
|
|
size++; /* round up to the next sector */
|
|
|
|
|
cdrom_image_viso_log("[%08X] %s => %" PRIu32 " + %" PRIu32 " sectors\n", entry, entry->path, viso->all_sectors, size);
|
|
|
|
|
|
|
|
|
|
/* Allocate sectors to this file. */
|
|
|
|
|
viso->all_sectors += size;
|
|
|
|
|
while (size-- > 0)
|
|
|
|
|
*entry_map_p++ = entry;
|
|
|
|
|
|
|
|
|
|
/* Move on to the next entry. */
|
2022-03-27 16:33:02 -03:00
|
|
|
prev_entry = entry;
|
|
|
|
|
entry = entry->next;
|
2022-03-26 22:17:09 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write final volume size to all volume descriptors. */
|
|
|
|
|
p = data;
|
|
|
|
|
VISO_LBE_32(p, viso->all_sectors);
|
|
|
|
|
for (int i = 0; i < (sizeof(viso->vol_size_offsets) / sizeof(viso->vol_size_offsets[0])); i++)
|
|
|
|
|
viso_pwrite(data, viso->vol_size_offsets[i], 8, 1, viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* Metadata processing is finished, read it back to memory. */
|
|
|
|
|
cdrom_image_viso_log("VISO: Reading back %d sectors of metadata\n", viso->metadata_sectors);
|
|
|
|
|
viso->metadata = (uint8_t *) calloc(viso->metadata_sectors, viso->sector_size);
|
|
|
|
|
if (!viso->metadata)
|
|
|
|
|
goto end;
|
|
|
|
|
fseeko64(viso->tf.file, 0, SEEK_SET);
|
|
|
|
|
uint64_t metadata_size = viso->metadata_sectors * viso->sector_size, metadata_remain = metadata_size;
|
|
|
|
|
while (metadata_remain > 0)
|
|
|
|
|
metadata_remain -= fread(viso->metadata + (metadata_size - metadata_remain), 1, MIN(metadata_remain, 2048), viso->tf.file);
|
|
|
|
|
|
|
|
|
|
/* We no longer need the temporary file; close and delete it. */
|
|
|
|
|
fclose(viso->tf.file);
|
|
|
|
|
viso->tf.file = NULL;
|
|
|
|
|
#ifndef ENABLE_CDROM_IMAGE_VISO_LOG
|
|
|
|
|
remove(nvr_path(viso->tf.fn));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* All good. */
|
|
|
|
|
*error = 0;
|
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
/* Set the function pointers. */
|
|
|
|
|
viso->tf.priv = viso;
|
|
|
|
|
if (!*error) {
|
|
|
|
|
cdrom_image_viso_log("VISO: Initialized\n");
|
|
|
|
|
viso->tf.read = viso_read;
|
|
|
|
|
viso->tf.get_length = viso_get_length;
|
|
|
|
|
viso->tf.close = viso_close;
|
|
|
|
|
return &viso->tf;
|
|
|
|
|
} else {
|
|
|
|
|
cdrom_image_viso_log("VISO: Initialization failed\n");
|
|
|
|
|
if (data)
|
|
|
|
|
free(data);
|
|
|
|
|
if (wtemp)
|
|
|
|
|
free(wtemp);
|
|
|
|
|
viso_close(&viso->tf);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|