mirror of https://git.sr.ht/~rabbits/porporo
Added file device
This commit is contained in:
parent
070deccf99
commit
6b63459187
2
build.sh
2
build.sh
|
@ -3,7 +3,7 @@
|
|||
# format code
|
||||
# clang-format -i src/porporo.c src/devices/*
|
||||
|
||||
SRC="src/uxn.c src/devices/system.c src/devices/screen.c src/devices/controller.c src/devices/mouse.c src/devices/datetime.c src/porporo.c"
|
||||
SRC="src/uxn.c src/devices/system.c src/devices/screen.c src/devices/controller.c src/devices/mouse.c src/devices/file.c src/devices/datetime.c src/porporo.c"
|
||||
|
||||
# remove old
|
||||
rm -f bin/porporo
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
#define _XOPEN_SOURCE 500
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <libiberty/libiberty.h>
|
||||
#define realpath(s, dummy) lrealpath(s)
|
||||
#define DIR_SEP_CHAR '\\'
|
||||
#define DIR_SEP_STR "\\"
|
||||
#define pathcmp(path1, path2, length) strncasecmp(path1, path2, length) /* strncasecmp provided by libiberty */
|
||||
#define notdriveroot(file_name) (file_name[0] != DIR_SEP_CHAR && ((strlen(file_name) > 2 && file_name[1] != ':') || strlen(file_name) <= 2))
|
||||
#else
|
||||
#define DIR_SEP_CHAR '/'
|
||||
#define DIR_SEP_STR "/"
|
||||
#define pathcmp(path1, path2, length) strncmp(path1, path2, length)
|
||||
#define notdriveroot(file_name) (file_name[0] != DIR_SEP_CHAR)
|
||||
#endif
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#include "../uxn.h"
|
||||
#include "file.h"
|
||||
|
||||
/*
|
||||
Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
DIR *dir;
|
||||
char current_filename[4096];
|
||||
struct dirent *de;
|
||||
enum { IDLE,
|
||||
FILE_READ,
|
||||
FILE_WRITE,
|
||||
DIR_READ } state;
|
||||
int outside_sandbox;
|
||||
} UxnFile;
|
||||
|
||||
static UxnFile uxn_file[POLYFILEY];
|
||||
|
||||
static void
|
||||
reset(UxnFile *c)
|
||||
{
|
||||
if(c->f != NULL) {
|
||||
fclose(c->f);
|
||||
c->f = NULL;
|
||||
}
|
||||
if(c->dir != NULL) {
|
||||
closedir(c->dir);
|
||||
c->dir = NULL;
|
||||
}
|
||||
c->de = NULL;
|
||||
c->state = IDLE;
|
||||
c->outside_sandbox = 0;
|
||||
}
|
||||
|
||||
static Uint16
|
||||
get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int fail_nonzero)
|
||||
{
|
||||
struct stat st;
|
||||
if(len < strlen(basename) + 8)
|
||||
return 0;
|
||||
if(stat(pathname, &st))
|
||||
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
|
||||
file_read_dir(UxnFile *c, char *dest, Uint16 len)
|
||||
{
|
||||
static char pathname[4352];
|
||||
char *p = dest;
|
||||
if(c->de == NULL) c->de = readdir(c->dir);
|
||||
for(; c->de != NULL; c->de = readdir(c->dir)) {
|
||||
Uint16 n;
|
||||
if(c->de->d_name[0] == '.' && c->de->d_name[1] == '\0')
|
||||
continue;
|
||||
if(strcmp(c->de->d_name, "..") == 0) {
|
||||
/* hide "sandbox/.." */
|
||||
char cwd[PATH_MAX] = {'\0'}, *t;
|
||||
/* Note there's [currently] no way of chdir()ing from uxn, so $PWD
|
||||
* is always the sandbox top level. */
|
||||
getcwd(cwd, sizeof(cwd));
|
||||
/* We already checked that c->current_filename exists so don't need a wrapper. */
|
||||
t = realpath(c->current_filename, NULL);
|
||||
if(strcmp(cwd, t) == 0) {
|
||||
free(t);
|
||||
continue;
|
||||
}
|
||||
free(t);
|
||||
}
|
||||
if(strlen(c->current_filename) + 1 + strlen(c->de->d_name) < sizeof(pathname))
|
||||
snprintf(pathname, sizeof(pathname), "%s/%s", c->current_filename, c->de->d_name);
|
||||
else
|
||||
pathname[0] = '\0';
|
||||
n = get_entry(p, len, pathname, c->de->d_name, 1);
|
||||
if(!n) break;
|
||||
p += n;
|
||||
len -= n;
|
||||
}
|
||||
return p - dest;
|
||||
}
|
||||
|
||||
static char *
|
||||
retry_realpath(const char *file_name)
|
||||
{
|
||||
char *r, p[PATH_MAX] = {'\0'}, *x;
|
||||
int fnlen;
|
||||
if(file_name == NULL) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
} else if((fnlen = strlen(file_name)) >= PATH_MAX) {
|
||||
errno = ENAMETOOLONG;
|
||||
return NULL;
|
||||
}
|
||||
if(notdriveroot(file_name)) {
|
||||
/* TODO: use a macro instead of '/' for absolute path first character so that other systems can work */
|
||||
/* if a relative path, prepend cwd */
|
||||
getcwd(p, sizeof(p));
|
||||
if(strlen(p) + strlen(DIR_SEP_STR) + fnlen >= PATH_MAX) {
|
||||
errno = ENAMETOOLONG;
|
||||
return NULL;
|
||||
}
|
||||
strcat(p, DIR_SEP_STR); /* TODO: use a macro instead of '/' for the path delimiter */
|
||||
}
|
||||
strcat(p, file_name);
|
||||
while((r = realpath(p, NULL)) == NULL) {
|
||||
if(errno != ENOENT)
|
||||
return NULL;
|
||||
x = strrchr(p, DIR_SEP_CHAR); /* TODO: path delimiter macro */
|
||||
if(x)
|
||||
*x = '\0';
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
file_check_sandbox(UxnFile *c)
|
||||
{
|
||||
char *x, *rp, cwd[PATH_MAX] = {'\0'};
|
||||
x = getcwd(cwd, sizeof(cwd));
|
||||
rp = retry_realpath(c->current_filename);
|
||||
if(rp == NULL || (x && pathcmp(cwd, rp, strlen(cwd)) != 0)) {
|
||||
c->outside_sandbox = 1;
|
||||
fprintf(stderr, "file warning: blocked attempt to access %s outside of sandbox\n", c->current_filename);
|
||||
}
|
||||
free(rp);
|
||||
}
|
||||
|
||||
static Uint16
|
||||
file_init(UxnFile *c, char *filename, size_t max_len, int override_sandbox)
|
||||
{
|
||||
char *p = c->current_filename;
|
||||
size_t len = sizeof(c->current_filename);
|
||||
reset(c);
|
||||
if(len > max_len) len = max_len;
|
||||
while(len) {
|
||||
if((*p++ = *filename++) == '\0') {
|
||||
if(!override_sandbox) /* override sandbox for loading roms */
|
||||
file_check_sandbox(c);
|
||||
return 0;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
c->current_filename[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint16
|
||||
file_read(UxnFile *c, void *dest, int len)
|
||||
{
|
||||
if(c->outside_sandbox) return 0;
|
||||
if(c->state != FILE_READ && c->state != DIR_READ) {
|
||||
reset(c);
|
||||
if((c->dir = opendir(c->current_filename)) != NULL)
|
||||
c->state = DIR_READ;
|
||||
else if((c->f = fopen(c->current_filename, "rb")) != NULL)
|
||||
c->state = FILE_READ;
|
||||
}
|
||||
if(c->state == FILE_READ)
|
||||
return fread(dest, 1, len, c->f);
|
||||
if(c->state == DIR_READ)
|
||||
return file_read_dir(c, dest, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint16
|
||||
file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
|
||||
{
|
||||
Uint16 ret = 0;
|
||||
if(c->outside_sandbox) return 0;
|
||||
if(c->state != FILE_WRITE) {
|
||||
reset(c);
|
||||
if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
|
||||
c->state = FILE_WRITE;
|
||||
}
|
||||
if(c->state == FILE_WRITE) {
|
||||
if((ret = fwrite(src, 1, len, c->f)) > 0 && fflush(c->f) != 0)
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Uint16
|
||||
file_stat(UxnFile *c, void *dest, Uint16 len)
|
||||
{
|
||||
char *basename = strrchr(c->current_filename, DIR_SEP_CHAR);
|
||||
if(c->outside_sandbox) return 0;
|
||||
if(basename != NULL)
|
||||
basename++;
|
||||
else
|
||||
basename = c->current_filename;
|
||||
return get_entry(dest, len, c->current_filename, basename, 0);
|
||||
}
|
||||
|
||||
static Uint16
|
||||
file_delete(UxnFile *c)
|
||||
{
|
||||
return c->outside_sandbox ? 0 : unlink(c->current_filename);
|
||||
}
|
||||
|
||||
/* IO */
|
||||
|
||||
void
|
||||
file_deo(Program *prg, Uint8 id, Uint8 *ram, Uint8 *d, Uint8 port)
|
||||
{
|
||||
UxnFile *c = &uxn_file[id];
|
||||
Uint16 addr, len, res;
|
||||
switch(port) {
|
||||
case 0x5:
|
||||
addr = PEEK2(d + 0x4);
|
||||
len = PEEK2(d + 0xa);
|
||||
if(len > 0x10000 - addr)
|
||||
len = 0x10000 - addr;
|
||||
res = file_stat(c, &ram[addr], len);
|
||||
POKE2(d + 0x2, res);
|
||||
break;
|
||||
case 0x6:
|
||||
res = file_delete(c);
|
||||
POKE2(d + 0x2, res);
|
||||
break;
|
||||
case 0x9:
|
||||
addr = PEEK2(d + 0x8);
|
||||
res = file_init(c, (char *)&ram[addr], 0x10000 - addr, 0);
|
||||
POKE2(d + 0x2, res);
|
||||
break;
|
||||
case 0xd:
|
||||
addr = PEEK2(d + 0xc);
|
||||
len = PEEK2(d + 0xa);
|
||||
if(len > 0x10000 - addr)
|
||||
len = 0x10000 - addr;
|
||||
res = file_read(c, &ram[addr], len);
|
||||
POKE2(d + 0x2, res);
|
||||
break;
|
||||
case 0xf:
|
||||
addr = PEEK2(d + 0xe);
|
||||
len = PEEK2(d + 0xa);
|
||||
if(len > 0x10000 - addr)
|
||||
len = 0x10000 - addr;
|
||||
res = file_write(c, &ram[addr], len, d[0x7]);
|
||||
POKE2(d + 0x2, res);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#define FILE_VERSION 1
|
||||
#define FILE_DEIMASK 0x0000
|
||||
#define FILE_DEOMASK 0xa260
|
||||
|
||||
#define POLYFILEY 2
|
||||
#define DEV_FILE0 0xa
|
||||
|
||||
void file_deo(Program *prg, Uint8 id, Uint8 *ram, Uint8 *d, Uint8 port);
|
|
@ -6,6 +6,7 @@
|
|||
#include "devices/screen.h"
|
||||
#include "devices/controller.h"
|
||||
#include "devices/mouse.h"
|
||||
#include "devices/file.h"
|
||||
#include "devices/datetime.h"
|
||||
|
||||
/*
|
||||
|
@ -31,7 +32,7 @@ static Uint8 *ram;
|
|||
static int plen;
|
||||
|
||||
static int reqdraw;
|
||||
static int dragx, dragy, movemode = 1;
|
||||
static int dragx, dragy, movemode;
|
||||
static int camerax, cameray;
|
||||
|
||||
static SDL_Window *gWindow = NULL;
|
||||
|
@ -353,6 +354,10 @@ static void
|
|||
handle_mouse(SDL_Event *event)
|
||||
{
|
||||
int i, desk = 1, x = event->motion.x - camerax, y = event->motion.y - cameray;
|
||||
if(event->type == SDL_MOUSEWHEEL) {
|
||||
mouse_scroll(&focused->u, &focused->u.dev[0x90], event->wheel.x, event->wheel.y);
|
||||
return;
|
||||
}
|
||||
for(i = plen - 1; i; i--) {
|
||||
Program *p = &programs[i];
|
||||
if(withinprogram(p, x, y)) {
|
||||
|
@ -373,8 +378,6 @@ handle_mouse(SDL_Event *event)
|
|||
mouse_down(&p->u, &p->u.dev[0x90], SDL_BUTTON(event->button.button));
|
||||
else if(event->type == SDL_MOUSEBUTTONUP)
|
||||
mouse_up(&p->u, &p->u.dev[0x90], SDL_BUTTON(event->button.button));
|
||||
else if(event->type == SDL_MOUSEWHEEL)
|
||||
mouse_scroll(&p->u, &p->u.dev[0x90], event->wheel.x, event->wheel.y);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -395,6 +398,7 @@ handle_mouse(SDL_Event *event)
|
|||
static void
|
||||
domouse(SDL_Event *event)
|
||||
{
|
||||
|
||||
switch(event->type) {
|
||||
case SDL_MOUSEBUTTONDOWN: handle_mouse(event); break;
|
||||
case SDL_MOUSEMOTION: handle_mouse(event); break;
|
||||
|
@ -529,6 +533,8 @@ emu_deo(Uxn *u, Uint8 addr, Uint8 value)
|
|||
case 0x20:
|
||||
screen_deo(prg, u->ram, &u->dev[d], p);
|
||||
break;
|
||||
case 0xa0: file_deo(prg, 0, u->ram, &u->dev[d], p); break;
|
||||
case 0xb0: file_deo(prg, 1, u->ram, &u->dev[d], p); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -550,7 +556,6 @@ porporo_key(char c)
|
|||
switch(c) {
|
||||
case 'd':
|
||||
movemode = !movemode;
|
||||
printf("%d\n", movemode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -577,6 +582,7 @@ main(int argc, char **argv)
|
|||
addprogram(20, 30, "bin/screen.pixel.rom");
|
||||
addprogram(150, 90, "bin/oekaki.rom");
|
||||
addprogram(650, 160, "bin/catclock.rom");
|
||||
addprogram(750, 300, "bin/left.rom");
|
||||
|
||||
connectports(prg_hello, prg_listen, 0x12, 0x18);
|
||||
connectports(prg_listen, porporo, 0x12, 0x18);
|
||||
|
@ -600,9 +606,12 @@ main(int argc, char **argv)
|
|||
while(SDL_PollEvent(&event) != 0) {
|
||||
switch(event.type) {
|
||||
case SDL_QUIT: quit(); break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEMOTION: domouse(&event); break;
|
||||
case SDL_MOUSEMOTION:
|
||||
domouse(&event);
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
if(focused == porporo) {
|
||||
porporo_key(event.text.text[0]);
|
||||
|
|
Loading…
Reference in New Issue