Virtual ISO: More filename calculation delegation to save more memory

This commit is contained in:
RichardG867
2022-03-31 17:08:20 -03:00
parent 5b555a2896
commit 9fcaa57264

View File

@@ -80,17 +80,16 @@ enum {
};
typedef struct _viso_entry_ {
char name_short[13];
union { /* save some memory */
uint64_t pt_offsets[4];
FILE *file;
};
union {
char name_short[13];
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 */
uint8_t name_joliet_len;
uint16_t pt_idx;
struct stat stats;
@@ -217,6 +216,7 @@ viso_convert_utf8(wchar_t *dest, const char *src, int buf_size)
case '"': \
case '%': \
case '&': \
case '\'': \
case '(': \
case ')': \
case '+': \
@@ -236,13 +236,13 @@ viso_convert_utf8(wchar_t *dest, const char *src, int buf_size)
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: \
case '\\': \
/* Not valid for A, D or filenames. */ \
if (charset <= VISO_CHARSET_FN) \
c = '_'; \
@@ -261,23 +261,23 @@ 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)
viso_fill_short_filename(char *data, const viso_entry_t *entry)
{
/* Get name and extension length. */
const char *ext_pos = strrchr(src, '.');
const char *ext_pos = strrchr(entry->basename, '.');
int name_len, ext_len;
if (ext_pos) {
name_len = ext_pos - src;
name_len = ext_pos - entry->basename;
ext_len = strlen(ext_pos);
} else {
name_len = strlen(src);
name_len = strlen(entry->basename);
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);
dest[name_copy_len] = '\0';
viso_write_string((uint8_t *) data, entry->basename, name_copy_len, VISO_CHARSET_D);
data[name_copy_len] = '\0';
/* Copy extension to temporary buffer. */
char ext[5] = { 0 };
@@ -298,25 +298,25 @@ viso_get_short_filename(viso_entry_t *dir, char *dest, const char *src)
int tail_len = -1;
if (i) {
tail_len = sprintf(tail, "~%d", i);
strcpy(&dest[MIN(name_copy_len, 8 - tail_len)], tail);
strcpy(&data[MIN(name_copy_len, 8 - tail_len)], tail);
}
/* Add extension to the filename if present. */
if (ext[0])
strcat(dest, ext);
strcat(data, ext);
/* Go through files in this directory to make sure this filename is unique. */
viso_entry_t *entry = dir->first_child;
while (entry) {
viso_entry_t *other_entry = entry->parent->first_child;
while (other_entry) {
/* Flag and stop if this filename was seen. */
if ((entry->name_short != dest) && !strcmp(dest, entry->name_short)) {
if ((other_entry->name_short != data) && !strcmp(data, other_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))
other_entry = other_entry->next;
if (other_entry && (other_entry->parent != entry->parent))
break;
}
@@ -327,6 +327,63 @@ viso_get_short_filename(viso_entry_t *dir, char *dest, const char *src)
return 1;
}
static size_t
viso_fill_fn_rr(uint8_t *data, const viso_entry_t *entry, size_t max_len)
{
/* Trim filename to max_len if needed. */
size_t len = strlen(entry->basename);
if (len > max_len) {
viso_write_string(data, entry->basename, max_len, VISO_CHARSET_FN);
/* Relocate extension if the original name exceeds the maximum length. */
if (!S_ISDIR(entry->stats.st_mode)) {
char *ext = strrchr(entry->basename, '.');
if (ext > entry->basename) {
len = strlen(ext);
if (len >= max_len)
len = max_len - 1; /* avoid creating a dotfile where there isn't one */
viso_write_string(data + (max_len - len), ext, len, VISO_CHARSET_FN);
}
}
return max_len;
} else {
viso_write_string(data, entry->basename, len, VISO_CHARSET_FN);
return len;
}
}
static size_t
viso_fill_fn_joliet(uint8_t *data, const viso_entry_t *entry, size_t max_len) /* note: receives and returns byte sizes */
{
/* Decode filename as UTF-8. */
size_t len = strlen(entry->basename);
wchar_t utf8dec[len + 1];
len = viso_convert_utf8(utf8dec, entry->basename, len + 1);
/* Trim decoded filename to max_len if needed. */
max_len /= 2;
if (len > max_len) {
viso_write_wstring((uint16_t *) data, utf8dec, max_len, VISO_CHARSET_FN);
/* Relocate extension if the original name exceeds the maximum length. */
if (!S_ISDIR(entry->stats.st_mode)) {
wchar_t *ext = wcsrchr(utf8dec, L'.');
if (ext > utf8dec) {
len = wcslen(ext);
if (len > max_len)
len = max_len;
viso_write_wstring(((uint16_t *) data) + (max_len - len), ext, len, VISO_CHARSET_FN);
}
}
return max_len * 2;
} else {
viso_write_wstring((uint16_t *) data, utf8dec, len, VISO_CHARSET_FN);
return len * 2;
}
}
static int
viso_fill_time(uint8_t *data, time_t time, int longform)
{
@@ -477,31 +534,9 @@ viso_fill_dir_record(uint8_t *data, viso_entry_t *entry, int type)
*r = 5; /* length */
*p++ = 1; /* version */
*p++ = 0; /* flags */
/* Trim Rock Ridge name to fit available space. */
size_t len = strlen(entry->basename),
max_len = 254 - (p - data);
if (len > max_len) {
*r += max_len;
viso_write_string(p, entry->basename, max_len, VISO_CHARSET_FN);
p += 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->basename, '.');
if (ext > entry->basename) {
len = strlen(ext);
if (len >= max_len)
len = max_len - 1; /* avoid creating a dotfile where there isn't one */
viso_write_string(p - len, ext, len, VISO_CHARSET_FN);
}
}
} else {
*r += len;
viso_write_string(p, entry->basename, len, VISO_CHARSET_FN);
p += len;
}
*p++ = 0; /* flags */
*r += viso_fill_fn_rr(p, entry, 254 - (p - data)); /* name */
p += (*r) - 5;
pad_susp:
if ((p - data) & 1) /* padding for odd SUSP section lengths */
*p++ = 0;
@@ -510,8 +545,7 @@ pad_susp:
case VISO_DIR_JOLIET:
q = p++; /* save location of the file ID length for later */
*q = entry->name_joliet_len * sizeof(entry->name_joliet[0]);
memcpy(p, entry->name_joliet, *q); /* file ID */
*q = viso_fill_fn_joliet(p, entry, 254 - (p - data));
p += *q;
if (!((*q) & 1)) /* padding for even file ID lengths */
@@ -641,19 +675,16 @@ 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;
viso_t *viso = (viso_t *) calloc(1, sizeof(viso_t));
uint8_t *data = NULL, *p;
*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)
data = calloc(2, viso->sector_size);
if (!data)
goto end;
/* Open temporary file. */
@@ -670,7 +701,7 @@ viso_init(const char *dirname, int *error)
cdrom_image_viso_log("VISO: Traversing directories:\n");
viso_entry_t *dir, *last_dir, *last_entry;
struct dirent *readdir_entry;
int max_len, len, name_len;
int len, name_len;
size_t dir_path_len;
/* Fill root directory entry. */
@@ -706,11 +737,10 @@ viso_init(const char *dirname, int *error)
/* Stat the current directory or parent directory. */
stat(i ? dir->parent->path : dir->path, &last_entry->stats);
/* Set short and long filenames. */
/* Set basename. */
strcpy(last_entry->name_short, 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);
cdrom_image_viso_log("[%08X] %s => %s\n", last_entry, dir->path, last_entry->basename);
}
/* Iterate through this directory's children. */
@@ -754,31 +784,9 @@ viso_init(const char *dirname, int *error)
viso->eltorito_entry = last_entry;
/* Set short filename. */
if (viso_get_short_filename(dir, last_entry->name_short, readdir_entry->d_name))
if (viso_fill_short_filename(last_entry->name_short, last_entry))
goto end;
/* 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;
len = viso_convert_utf8(wtemp, readdir_entry->d_name, wtemp_len);
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';
last_entry->name_joliet_len = len;
cdrom_image_viso_log("[%08X] %s => [%-12s] %s\n", last_entry, dir->path, last_entry->name_short, last_entry->basename);
/* If this is a directory, add it to the traversal list. */
@@ -822,7 +830,8 @@ next_dir:
if (i) {
viso_write_wstring((uint16_t *) p, EMU_NAME_W, 16, VISO_CHARSET_A); /* system ID */
p += 32;
mbstowcs(wtemp, basename, 16);
wchar_t wtemp[16];
viso_convert_utf8(wtemp, basename, 16);
viso_write_wstring((uint16_t *) p, wtemp, 16, VISO_CHARSET_D); /* volume ID */
p += 32;
} else {
@@ -1051,35 +1060,38 @@ next_dir:
continue;
}
cdrom_image_viso_log("[%08X] %s => %s\n", dir, dir->path, (i & 2) ? dir->basename : dir->name_short);
cdrom_image_viso_log("[%08X] %s => %s\n", dir, dir->path, ((i & 2) || (dir == viso->root_dir)) ? dir->basename : 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 */
p = data + 1; /* skip ID length for now */
*p++ = 0; /* extended attribute length */
*((uint32_t *) p) = 0; /* extent location (filled in later) */
p += 4;
*((uint16_t *) p) = (i & 1) ? cpu_to_be16(dir->parent->pt_idx) : cpu_to_le16(dir->parent->pt_idx); /* parent directory number */
p += 2;
if (dir == viso->root_dir) { /* directory ID and length */
data[0] = 1;
else if (i & 2)
data[0] = dir->name_joliet_len * sizeof(dir->name_joliet[0]);
else
*p = 0x00;
} else if (i & 2) {
data[0] = viso_fill_fn_joliet(p, dir, 255);
} else {
data[0] = strlen(dir->name_short);
memcpy(p, dir->name_short, data[0]);
}
p += data[0];
data[1] = 0; /* extended attribute length */
*((uint32_t *) &data[2]) = 0; /* extent location (filled in later) */
*((uint16_t *) &data[6]) = (i & 1) ? cpu_to_be16(dir->parent->pt_idx) : cpu_to_le16(dir->parent->pt_idx); /* parent directory number */
if (dir == viso->root_dir) /* directory ID */
data[8] = 0;
else if (i & 2)
memcpy(&data[8], dir->name_joliet, data[0]);
else
memcpy(&data[8], dir->name_short, data[0]);
data[8 + data[0]] = 0; /* padding for odd directory ID lengths */
if ((p - data) & 1) /* padding for odd directory ID lengths */
*p++ = 0x00;
/* Write path table entry. */
fwrite(data, 8 + data[0] + (data[0] & 1), 1, viso->tf.file);
fwrite(data, p - data, 1, viso->tf.file);
/* Increment path table index and stop if it overflows. */
if (++pt_idx == 0)
@@ -1133,7 +1145,7 @@ next_dir:
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 */
if (i) /* clear union if we no longer need path table offsets */
if (i) /* overwrite pt_offsets in the union if we no longer need them */
dir->file = NULL;
/* Go through entries in this directory. */
@@ -1144,8 +1156,8 @@ next_dir:
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->basename : entry->name_short);
((dir_type == VISO_DIR_PARENT) ? dir->parent->path : ((dir_type < VISO_DIR_PARENT) ? dir->path : entry->path)),
((dir_type == VISO_DIR_PARENT) ? ".." : ((dir_type < VISO_DIR_PARENT) ? "." : (i ? entry->basename : entry->name_short))));
/* Fill directory record. */
viso_fill_dir_record(data, entry, dir_type);
@@ -1158,7 +1170,7 @@ next_dir:
fwrite(p, write, 1, viso->tf.file);
}
/* Write this entry's record's offset. */
/* Write this entry's record's offset. This overwrites name_short in the union. */
entry->dr_offsets[i] = ftello64(viso->tf.file);
/* Write data related to the . and .. pseudo-subdirectories,
@@ -1313,8 +1325,6 @@ end:
cdrom_image_viso_log("VISO: Initialization failed\n");
if (data)
free(data);
if (wtemp)
free(wtemp);
viso_close(&viso->tf);
return NULL;
}