Ensure that all file operations correctly wrap in memory.
The Varvara spec says that memory accesses should wrap. Previously the file device ignored this by attempting to truncate all reads and writes that would go beyond 0xffff. There may be cases where memory outside uxn.ram could be accessed. After this change, all addresses read from and written to should correctly be wrapped (i.e. uxn.ram[addr & 0xffff]). This change also fixes a bug with hexadecimal sizes which the previous patch introduced.
This commit is contained in:
parent
df51651789
commit
6dabcecafb
|
@ -76,26 +76,70 @@ reset(UxnFile *c)
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint16
|
static Uint16
|
||||||
get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int fail_nonzero)
|
stat_fill(Uint16 addr, Uint16 len, char c)
|
||||||
{
|
{
|
||||||
struct stat st;
|
Uint16 i;
|
||||||
if(len < strlen(basename) + 8)
|
for (i = 0; i < len; i++)
|
||||||
return 0;
|
uxn.ram[(addr + i) & 0xffff] = c;
|
||||||
if(stat(pathname, &st))
|
return len;
|
||||||
return fail_nonzero ? snprintf(p, len, "!!!! %s\n", basename) : 0;
|
|
||||||
else if(S_ISDIR(st.st_mode))
|
|
||||||
return snprintf(p, len, "---- %s/\n", basename);
|
|
||||||
else if(st.st_size < 0x10000)
|
|
||||||
return snprintf(p, len, "%04x %s\n", (unsigned int)st.st_size, basename);
|
|
||||||
else
|
|
||||||
return snprintf(p, len, "???? %s\n", basename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint16
|
static Uint16
|
||||||
file_read_dir(UxnFile *c, char *dest, Uint16 len)
|
stat_size(Uint16 addr, Uint16 len, off_t size)
|
||||||
|
{
|
||||||
|
Uint16 i;
|
||||||
|
for (i = 1; i <= len; i++) {
|
||||||
|
char c = '0' + (Uint8)(size & 0xf);
|
||||||
|
if (c > '9') c += 39;
|
||||||
|
uxn.ram[(addr + len - i) & 0xffff] = c;
|
||||||
|
size = size >> 4;
|
||||||
|
}
|
||||||
|
if (size > 0)
|
||||||
|
stat_fill(addr, len, '?');
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint16
|
||||||
|
stat_entry(Uint16 addr, char sym, off_t size, const char *s)
|
||||||
|
{
|
||||||
|
Uint16 pos = addr;
|
||||||
|
pos += sym ? stat_fill(pos, 4, sym) : stat_size(pos, 4, size);
|
||||||
|
uxn.ram[(pos++) & 0xffff] = ' ';
|
||||||
|
while(*s)
|
||||||
|
uxn.ram[(pos++) & 0xffff] = *(s++);
|
||||||
|
if (sym == '-')
|
||||||
|
uxn.ram[(pos++) & 0xffff] = '/';
|
||||||
|
uxn.ram[(pos++) & 0xffff] = '\n';
|
||||||
|
return pos - addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint16
|
||||||
|
get_entry(Uint16 addr, Uint16 len, const char *pathname, const char *basename)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
int row_size = strlen(basename) + 6; /* 4 bytes, ' ', basename, '\n' */
|
||||||
|
char sym = 0;
|
||||||
|
off_t size = 0;
|
||||||
|
|
||||||
|
if(stat(pathname, &st))
|
||||||
|
sym = '!';
|
||||||
|
else if(S_ISDIR(st.st_mode)) {
|
||||||
|
sym = '-';
|
||||||
|
row_size++; /* directories have a '/' suffix */
|
||||||
|
} else if(st.st_size >= 0x10000)
|
||||||
|
sym = '?';
|
||||||
|
else
|
||||||
|
size = st.st_size;
|
||||||
|
|
||||||
|
return len < row_size ? 0 : stat_entry(addr, sym, size, basename);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint16
|
||||||
|
file_read_dir(UxnFile *c, Uint16 addr, Uint16 len)
|
||||||
{
|
{
|
||||||
static char pathname[4352];
|
static char pathname[4352];
|
||||||
char *p = dest;
|
Uint16 pos = addr;
|
||||||
|
char cwd[PATH_MAX] = {'\0'}, *t;
|
||||||
if(c->de == NULL) c->de = readdir(c->dir);
|
if(c->de == NULL) c->de = readdir(c->dir);
|
||||||
for(; c->de != NULL; c->de = readdir(c->dir)) {
|
for(; c->de != NULL; c->de = readdir(c->dir)) {
|
||||||
Uint16 n;
|
Uint16 n;
|
||||||
|
@ -103,7 +147,6 @@ file_read_dir(UxnFile *c, char *dest, Uint16 len)
|
||||||
continue;
|
continue;
|
||||||
if(strcmp(c->de->d_name, "..") == 0) {
|
if(strcmp(c->de->d_name, "..") == 0) {
|
||||||
/* hide "sandbox/.." */
|
/* hide "sandbox/.." */
|
||||||
char cwd[PATH_MAX] = {'\0'}, *t;
|
|
||||||
/* Note there's [currently] no way of chdir()ing from uxn, so $PWD
|
/* Note there's [currently] no way of chdir()ing from uxn, so $PWD
|
||||||
* is always the sandbox top level. */
|
* is always the sandbox top level. */
|
||||||
getcwd(cwd, sizeof(cwd));
|
getcwd(cwd, sizeof(cwd));
|
||||||
|
@ -119,12 +162,12 @@ file_read_dir(UxnFile *c, char *dest, Uint16 len)
|
||||||
snprintf(pathname, sizeof(pathname), "%s/%s", c->current_filename, c->de->d_name);
|
snprintf(pathname, sizeof(pathname), "%s/%s", c->current_filename, c->de->d_name);
|
||||||
else
|
else
|
||||||
pathname[0] = '\0';
|
pathname[0] = '\0';
|
||||||
n = get_entry(p, len, pathname, c->de->d_name, 1);
|
n = get_entry(pos, len, pathname, c->de->d_name);
|
||||||
if(!n) break;
|
if(!n) break;
|
||||||
p += n;
|
pos += n;
|
||||||
len -= n;
|
len -= n;
|
||||||
}
|
}
|
||||||
return p - dest;
|
return pos - addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
|
@ -176,26 +219,35 @@ file_check_sandbox(UxnFile *c)
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint16
|
static Uint16
|
||||||
file_init(UxnFile *c, char *filename, size_t max_len, int override_sandbox)
|
file_init(UxnFile *c, Uint16 addr)
|
||||||
{
|
{
|
||||||
char *p = c->current_filename;
|
char *p = c->current_filename;
|
||||||
size_t len = sizeof(c->current_filename);
|
Uint16 i = addr;
|
||||||
reset(c);
|
reset(c);
|
||||||
if(len > max_len) len = max_len;
|
for(;; p++, i++) {
|
||||||
while(len) {
|
if((*p = uxn.ram[i & 0xffff]) == '\0') {
|
||||||
if((*p++ = *filename++) == '\0') {
|
|
||||||
if(!override_sandbox) /* override sandbox for loading roms */
|
|
||||||
file_check_sandbox(c);
|
file_check_sandbox(c);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
len--;
|
|
||||||
}
|
}
|
||||||
c->current_filename[0] = '\0';
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint16
|
static Uint16
|
||||||
file_read(UxnFile *c, void *dest, int len)
|
file_read_file(Uint16 addr, int len, FILE *f)
|
||||||
|
{
|
||||||
|
int total = addr + len;
|
||||||
|
if (total <= 0x10000) {
|
||||||
|
return fread(&uxn.ram[addr], 1, len, f);
|
||||||
|
} else {
|
||||||
|
size_t n1 = fread(&uxn.ram[addr], 1, 0x10000 - addr, f);
|
||||||
|
size_t n2 = fread(&uxn.ram[0], 1, total - 0x10000, f);
|
||||||
|
return n1 + n2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint16
|
||||||
|
file_read(UxnFile *c, Uint16 addr, int len)
|
||||||
{
|
{
|
||||||
if(c->outside_sandbox) return 0;
|
if(c->outside_sandbox) return 0;
|
||||||
if(c->state != FILE_READ && c->state != DIR_READ) {
|
if(c->state != FILE_READ && c->state != DIR_READ) {
|
||||||
|
@ -206,9 +258,9 @@ file_read(UxnFile *c, void *dest, int len)
|
||||||
c->state = FILE_READ;
|
c->state = FILE_READ;
|
||||||
}
|
}
|
||||||
if(c->state == FILE_READ)
|
if(c->state == FILE_READ)
|
||||||
return fread(dest, 1, len, c->f);
|
return file_read_file(addr, len, c->f);
|
||||||
if(c->state == DIR_READ)
|
if(c->state == DIR_READ)
|
||||||
return file_read_dir(c, dest, len);
|
return file_read_dir(c, addr, len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +297,22 @@ ensure_parent_dirs(char *p)
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint16
|
static Uint16
|
||||||
file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
|
file_write_file(Uint16 addr, int len, FILE *f)
|
||||||
|
{
|
||||||
|
int total = addr + len;
|
||||||
|
fflush(stdout);
|
||||||
|
if (total <= 0x10000) {
|
||||||
|
Uint16 n = fwrite(&uxn.ram[addr], 1, len, f);
|
||||||
|
return fflush(f) == 0 ? n : 0;
|
||||||
|
} else {
|
||||||
|
Uint16 n1 = fwrite(&uxn.ram[addr], 1, 0x10000 - addr, f);
|
||||||
|
Uint16 n2 = fwrite(&uxn.ram[0], 1, total - 0x10000, f);
|
||||||
|
return fflush(f) == 0 ? n1 + n2 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint16
|
||||||
|
file_write(UxnFile *c, Uint16 addr, Uint16 len, Uint8 flags)
|
||||||
{
|
{
|
||||||
Uint16 ret = 0;
|
Uint16 ret = 0;
|
||||||
if(c->outside_sandbox) return 0;
|
if(c->outside_sandbox) return 0;
|
||||||
|
@ -258,8 +325,7 @@ file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
|
||||||
c->state = FILE_WRITE;
|
c->state = FILE_WRITE;
|
||||||
}
|
}
|
||||||
if(c->state == FILE_WRITE) {
|
if(c->state == FILE_WRITE) {
|
||||||
if((ret = fwrite(src, 1, len, c->f)) > 0 && fflush(c->f) != 0)
|
ret = file_write_file(addr, len, c->f);
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
if (c->state == DIR_WRITE) {
|
if (c->state == DIR_WRITE) {
|
||||||
ret = dir_exists(c->current_filename);
|
ret = dir_exists(c->current_filename);
|
||||||
|
@ -268,38 +334,17 @@ file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint16
|
static Uint16
|
||||||
stat_fill(Uint8 *dest, Uint16 len, char c)
|
file_stat(UxnFile *c, Uint16 addr, Uint16 len)
|
||||||
{
|
|
||||||
Uint16 i;
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
*(dest++) = c;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint16
|
|
||||||
stat_size(Uint8 *dest, Uint16 len, off_t size)
|
|
||||||
{
|
|
||||||
Uint16 i;
|
|
||||||
dest += len - 1;
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
*(dest--) = '0' + (Uint8)(size & 0xf);
|
|
||||||
size = size >> 4;
|
|
||||||
}
|
|
||||||
return size == 0 ? len : stat_fill(dest, len, '?');
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint16
|
|
||||||
file_stat(UxnFile *c, void *dest, Uint16 len)
|
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if(c->outside_sandbox)
|
if(c->outside_sandbox)
|
||||||
return 0;
|
return 0;
|
||||||
else if(stat(c->current_filename, &st))
|
else if(stat(c->current_filename, &st))
|
||||||
return stat_fill(dest, len, '!');
|
return stat_fill(addr, len, '!');
|
||||||
else if(S_ISDIR(st.st_mode))
|
else if(S_ISDIR(st.st_mode))
|
||||||
return stat_fill(dest, len, '-');
|
return stat_fill(addr, len, '-');
|
||||||
else
|
else
|
||||||
return stat_size(dest, len, st.st_size);
|
return stat_size(addr, len, st.st_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint16
|
static Uint16
|
||||||
|
@ -318,9 +363,7 @@ file_deo(Uint8 port)
|
||||||
case 0xa5:
|
case 0xa5:
|
||||||
addr = PEEK2(&uxn.dev[0xa4]);
|
addr = PEEK2(&uxn.dev[0xa4]);
|
||||||
len = PEEK2(&uxn.dev[0xaa]);
|
len = PEEK2(&uxn.dev[0xaa]);
|
||||||
if(len > 0x10000 - addr)
|
res = file_stat(&uxn_file[0], addr, len);
|
||||||
len = 0x10000 - addr;
|
|
||||||
res = file_stat(&uxn_file[0], &uxn.ram[addr], len);
|
|
||||||
POKE2(&uxn.dev[0xa2], res);
|
POKE2(&uxn.dev[0xa2], res);
|
||||||
break;
|
break;
|
||||||
case 0xa6:
|
case 0xa6:
|
||||||
|
@ -329,32 +372,26 @@ file_deo(Uint8 port)
|
||||||
break;
|
break;
|
||||||
case 0xa9:
|
case 0xa9:
|
||||||
addr = PEEK2(&uxn.dev[0xa8]);
|
addr = PEEK2(&uxn.dev[0xa8]);
|
||||||
res = file_init(&uxn_file[0], (char *)&uxn.ram[addr], 0x10000 - addr, 0);
|
res = file_init(&uxn_file[0], addr);
|
||||||
POKE2(&uxn.dev[0xa2], res);
|
POKE2(&uxn.dev[0xa2], res);
|
||||||
break;
|
break;
|
||||||
case 0xad:
|
case 0xad:
|
||||||
addr = PEEK2(&uxn.dev[0xac]);
|
addr = PEEK2(&uxn.dev[0xac]);
|
||||||
len = PEEK2(&uxn.dev[0xaa]);
|
len = PEEK2(&uxn.dev[0xaa]);
|
||||||
if(len > 0x10000 - addr)
|
res = file_read(&uxn_file[0], addr, len);
|
||||||
len = 0x10000 - addr;
|
|
||||||
res = file_read(&uxn_file[0], &uxn.ram[addr], len);
|
|
||||||
POKE2(&uxn.dev[0xa2], res);
|
POKE2(&uxn.dev[0xa2], res);
|
||||||
break;
|
break;
|
||||||
case 0xaf:
|
case 0xaf:
|
||||||
addr = PEEK2(&uxn.dev[0xae]);
|
addr = PEEK2(&uxn.dev[0xae]);
|
||||||
len = PEEK2(&uxn.dev[0xaa]);
|
len = PEEK2(&uxn.dev[0xaa]);
|
||||||
if(len > 0x10000 - addr)
|
res = file_write(&uxn_file[0], addr, len, uxn.dev[0xa7]);
|
||||||
len = 0x10000 - addr;
|
|
||||||
res = file_write(&uxn_file[0], &uxn.ram[addr], len, uxn.dev[0xa7]);
|
|
||||||
POKE2(&uxn.dev[0xa2], res);
|
POKE2(&uxn.dev[0xa2], res);
|
||||||
break;
|
break;
|
||||||
/* File 2 */
|
/* File 2 */
|
||||||
case 0xb5:
|
case 0xb5:
|
||||||
addr = PEEK2(&uxn.dev[0xb4]);
|
addr = PEEK2(&uxn.dev[0xb4]);
|
||||||
len = PEEK2(&uxn.dev[0xba]);
|
len = PEEK2(&uxn.dev[0xba]);
|
||||||
if(len > 0x10000 - addr)
|
res = file_stat(&uxn_file[1], addr, len);
|
||||||
len = 0x10000 - addr;
|
|
||||||
res = file_stat(&uxn_file[1], &uxn.ram[addr], len);
|
|
||||||
POKE2(&uxn.dev[0xb2], res);
|
POKE2(&uxn.dev[0xb2], res);
|
||||||
break;
|
break;
|
||||||
case 0xb6:
|
case 0xb6:
|
||||||
|
@ -363,23 +400,19 @@ file_deo(Uint8 port)
|
||||||
break;
|
break;
|
||||||
case 0xb9:
|
case 0xb9:
|
||||||
addr = PEEK2(&uxn.dev[0xb8]);
|
addr = PEEK2(&uxn.dev[0xb8]);
|
||||||
res = file_init(&uxn_file[1], (char *)&uxn.ram[addr], 0x10000 - addr, 0);
|
res = file_init(&uxn_file[1], addr);
|
||||||
POKE2(&uxn.dev[0xb2], res);
|
POKE2(&uxn.dev[0xb2], res);
|
||||||
break;
|
break;
|
||||||
case 0xbd:
|
case 0xbd:
|
||||||
addr = PEEK2(&uxn.dev[0xbc]);
|
addr = PEEK2(&uxn.dev[0xbc]);
|
||||||
len = PEEK2(&uxn.dev[0xba]);
|
len = PEEK2(&uxn.dev[0xba]);
|
||||||
if(len > 0x10000 - addr)
|
res = file_read(&uxn_file[1], addr, len);
|
||||||
len = 0x10000 - addr;
|
|
||||||
res = file_read(&uxn_file[1], &uxn.ram[addr], len);
|
|
||||||
POKE2(&uxn.dev[0xb2], res);
|
POKE2(&uxn.dev[0xb2], res);
|
||||||
break;
|
break;
|
||||||
case 0xbf:
|
case 0xbf:
|
||||||
addr = PEEK2(&uxn.dev[0xbe]);
|
addr = PEEK2(&uxn.dev[0xbe]);
|
||||||
len = PEEK2(&uxn.dev[0xba]);
|
len = PEEK2(&uxn.dev[0xba]);
|
||||||
if(len > 0x10000 - addr)
|
res = file_write(&uxn_file[1], addr, len, uxn.dev[0xb7]);
|
||||||
len = 0x10000 - addr;
|
|
||||||
res = file_write(&uxn_file[1], &uxn.ram[addr], len, uxn.dev[0xb7]);
|
|
||||||
POKE2(&uxn.dev[0xb2], res);
|
POKE2(&uxn.dev[0xb2], res);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue