[Debugger] Add file system interface to the JS API

This commit is contained in:
shygoo 2018-02-18 15:37:03 -06:00
parent 3388a053f3
commit d409dbc86e
4 changed files with 795 additions and 26 deletions

View File

@ -56,6 +56,133 @@ const ADDR_ANY_RDRAM_UNC = new AddressRange(0xA0000000, 0xA0800000)
const ADDR_ANY_CART_ROM = new AddressRange(0x90000000, 0x96000000) const ADDR_ANY_CART_ROM = new AddressRange(0x90000000, 0x96000000)
const ADDR_ANY_CART_ROM_UNC = new AddressRange(0xB0000000, 0xB6000000) const ADDR_ANY_CART_ROM_UNC = new AddressRange(0xB0000000, 0xB6000000)
const fs = {
open: function(path, mode)
{
if (["r", "rb", "w", "wb", "a", "ab", "r+", "rb+", "r+b", "w+", "wb+", "w+b", "a+", "ab+", "a+b"].indexOf(mode) == -1)
{
return false
}
return _native.fsOpen(path, mode)
},
close: function(fd)
{
return _native.fsClose(fd)
},
write: function(fd, buffer, offset, length, position)
{
return _native.fsWrite(fd, buffer, offset, length, position)
},
writeFile: function(path, buffer)
{
var fd = fs.open(path, 'wb')
if (fd == false)
{
return false
}
fs.write(fd, buffer)
fs.close(fd)
return true
},
read: function (fd, buffer, offset, length, position)
{
return _native.fsRead(fd, buffer, offset, length, position)
},
readFile: function(path)
{
var fd = fs.open(path, 'rb')
if(fd == false)
{
return false
}
var stats = fs.fstat(fd);
var buf = new Buffer(stats.size)
fs.read(fd, buf, 0, stats.size, 0)
return buf
},
fstat: function(fd)
{
var rawStats = _native.fsFStat(fd)
if (rawStats == false)
{
return false
}
return new fs.Stats(rawStats)
},
stat: function(path)
{
var rawStats = _native.fsStat(path)
if (rawStats == false)
{
return false
}
return new fs.Stats(rawStats)
},
mkdir: function(path)
{
return _native.fsMkDir(path)
},
rmdir: function(path)
{
return _native.fsRmDir(path)
},
readdir: function(path)
{
return _native.fsReadDir(path)
},
unlink: function(path)
{
return _native.fsUnlink(path)
},
Stats: function(rawstats)
{
this.dev = rawstats.dev
this.ino = rawstats.ino
this.mode = rawstats.mode
this.nlink = rawstats.nlink
this.uid = rawstats.uid
this.gid = rawstats.gid
this.rdev = rawstats.rdev
this.size = rawstats.size
this.atimeMs = rawstats.atimeMs
this.mtimeMs = rawstats.mtimeMs
this.ctimeMs = rawstats.ctimeMs
this.atime = new Date(this.atimeMs)
this.mtime = new Date(this.mtimeMs)
this.ctime = new Date(this.ctimeMs)
Object.freeze(this)
}
}
fs.Stats.S_IFMT = 0xF000
fs.Stats.S_IFDIR = 0x4000
fs.Stats.S_IFCHR = 0x2000
fs.Stats.S_IFIFO = 0x1000
fs.Stats.S_IFREG = 0x8000
fs.Stats.S_IREAD = 0x0100
fs.Stats.S_IWRITE = 0x0080
fs.Stats.S_IEXEC = 0x0040
fs.Stats.prototype.isDirectory = function()
{
return ((this.mode & fs.Stats.S_IFMT) == fs.Stats.S_IFDIR)
}
fs.Stats.prototype.isFile = function()
{
return ((this.mode & fs.Stats.S_IFMT) == fs.Stats.S_IFREG)
}
Object.freeze(fs.Stats)
Object.freeze(fs.Stats.prototype)
const system = { const system = {
pause: function() pause: function()
{ {

View File

@ -1,4 +1,5 @@
#include <stdafx.h> #include <stdafx.h>
#include <sys/stat.h>
#include "ScriptInstance.h" #include "ScriptInstance.h"
#include "ScriptSystem.h" #include "ScriptSystem.h"
@ -69,8 +70,7 @@ void CScriptInstance::ForceStop()
{ {
// Close all files and delete all hooked callbacks // Close all files and delete all hooked callbacks
EnterCriticalSection(&m_CriticalSection); EnterCriticalSection(&m_CriticalSection);
m_ScriptSystem->ClearCallbacksForInstance(this); CleanUp();
CloseAllFiles();
LeaveCriticalSection(&m_CriticalSection); LeaveCriticalSection(&m_CriticalSection);
SetState(STATE_STOPPED); SetState(STATE_STOPPED);
} }
@ -164,10 +164,18 @@ void CScriptInstance::StartScriptProc()
} }
else else
{ {
CleanUp();
SetState(STATE_STOPPED); SetState(STATE_STOPPED);
} }
} }
void CScriptInstance::CleanUp()
{
m_ScriptSystem->ClearCallbacksForInstance(this);
CloseAllAsyncFiles();
CloseAllFiles();
}
void CScriptInstance::StartEventLoop() void CScriptInstance::StartEventLoop()
{ {
SetState(STATE_RUNNING); SetState(STATE_RUNNING);
@ -192,6 +200,7 @@ void CScriptInstance::StartEventLoop()
RemoveListener(lpListener); RemoveListener(lpListener);
} }
CleanUp();
SetState(STATE_STOPPED); SetState(STATE_STOPPED);
} }
@ -235,23 +244,23 @@ bool CScriptInstance::HaveEvents()
m_ScriptSystem->HasCallbacksForInstance(this); m_ScriptSystem->HasCallbacksForInstance(this);
} }
void CScriptInstance::AddFile(HANDLE fd, bool bSocket) void CScriptInstance::AddAsyncFile(HANDLE fd, bool bSocket)
{ {
IOFD iofd; IOFD iofd;
iofd.fd = fd; iofd.fd = fd;
iofd.iocp = CreateIoCompletionPort(fd, m_hIOCompletionPort, (ULONG_PTR)fd, 0); iofd.iocp = CreateIoCompletionPort(fd, m_hIOCompletionPort, (ULONG_PTR)fd, 0);
iofd.bSocket = bSocket; iofd.bSocket = bSocket;
m_Files.push_back(iofd); m_AsyncFiles.push_back(iofd);
} }
void CScriptInstance::CloseFile(HANDLE fd) void CScriptInstance::CloseAsyncFile(HANDLE fd)
{ {
// Causes EVT_READ with length 0 // Causes EVT_READ with length 0
// Not safe to remove listeners until then // Not safe to remove listeners until then
for (uint32_t i = 0; i < m_Files.size(); i++) for (uint32_t i = 0; i < m_AsyncFiles.size(); i++)
{ {
IOFD iofd = m_Files[i]; IOFD iofd = m_AsyncFiles[i];
if (iofd.fd != fd) if (iofd.fd != fd)
{ {
continue; continue;
@ -270,14 +279,14 @@ void CScriptInstance::CloseFile(HANDLE fd)
} }
} }
void CScriptInstance::CloseAllFiles() void CScriptInstance::CloseAllAsyncFiles()
{ {
// Causes EVT_READ with length 0 // Causes EVT_READ with length 0
// Not safe to remove listeners until then // Not safe to remove listeners until then
for (uint32_t i = 0; i < m_Files.size(); i++) for (uint32_t i = 0; i < m_AsyncFiles.size(); i++)
{ {
IOFD iofd = m_Files[i]; IOFD iofd = m_AsyncFiles[i];
// Close file handle // Close file handle
if (iofd.bSocket) if (iofd.bSocket)
@ -291,18 +300,18 @@ void CScriptInstance::CloseAllFiles()
} }
} }
void CScriptInstance::RemoveFile(HANDLE fd) void CScriptInstance::RemoveAsyncFile(HANDLE fd)
{ {
// Stop tracking an fd and remove all of its listeners // Stop tracking an fd and remove all of its listeners
for (uint32_t i = 0; i < m_Files.size(); i++) for (uint32_t i = 0; i < m_AsyncFiles.size(); i++)
{ {
IOFD iofd = m_Files[i]; IOFD iofd = m_AsyncFiles[i];
if (iofd.fd != fd) if (iofd.fd != fd)
{ {
continue; continue;
} }
m_Files.erase(m_Files.begin() + i); m_AsyncFiles.erase(m_AsyncFiles.begin() + i);
RemoveListenersByFd(fd); RemoveListenersByFd(fd);
break; break;
} }
@ -311,7 +320,7 @@ void CScriptInstance::RemoveFile(HANDLE fd)
HANDLE CScriptInstance::CreateSocket() HANDLE CScriptInstance::CreateSocket()
{ {
HANDLE fd = (HANDLE)WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); HANDLE fd = (HANDLE)WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
AddFile(fd, true); AddAsyncFile(fd, true);
return fd; return fd;
} }
@ -398,7 +407,7 @@ void CScriptInstance::InvokeListenerCallback(IOLISTENER* lpListener)
else else
{ {
// handle must have closed, safe to untrack fd and remove all associated listeners // handle must have closed, safe to untrack fd and remove all associated listeners
RemoveFile(lpListener->fd); RemoveAsyncFile(lpListener->fd);
// pass null to callback // pass null to callback
duk_push_null(m_Ctx); duk_push_null(m_Ctx);
@ -476,6 +485,67 @@ void CALLBACK CScriptInstance::EvalAsyncCallback(ULONG_PTR _pendingEval)
delete evalWait; delete evalWait;
} }
bool CScriptInstance::AddFile(const char* path, const char* mode, int* fd)
{
FILE* fp = fopen(path, mode);
if (fp == NULL)
{
return false;
}
FILE_FD filefd;
filefd.fp = fp;
filefd.fd = _fileno(fp);
m_Files.push_back(filefd);
*fd = filefd.fd;
return true;
}
void CScriptInstance::CloseFile(int fd)
{
size_t nFiles = m_Files.size();
for (size_t i = 0; i < nFiles; i++)
{
FILE_FD* filefd = &m_Files[i];
if (filefd->fd == fd)
{
fclose(filefd->fp);
m_Files.erase(m_Files.begin() + i);
return;
}
}
}
void CScriptInstance::CloseAllFiles()
{
size_t nFiles = m_Files.size();
for (size_t i = 0; i < nFiles; i++)
{
fclose(m_Files[i].fp);
m_Files.erase(m_Files.begin() + i);
}
}
FILE* CScriptInstance::GetFilePointer(int fd)
{
size_t nFiles = m_Files.size();
for (size_t i = 0; i < nFiles; i++)
{
FILE_FD* filefd = &m_Files[i];
if (filefd->fd == fd)
{
return filefd->fp;
}
}
return NULL;
}
const char* CScriptInstance::EvalFile(const char* jsPath) const char* CScriptInstance::EvalFile(const char* jsPath)
{ {
int result = duk_peval_file(m_Ctx, jsPath); int result = duk_peval_file(m_Ctx, jsPath);
@ -554,7 +624,7 @@ duk_ret_t CScriptInstance::js_ioClose(duk_context* ctx)
CScriptInstance* _this = FetchInstance(ctx); CScriptInstance* _this = FetchInstance(ctx);
HANDLE fd = (HANDLE)duk_get_uint(ctx, 0); HANDLE fd = (HANDLE)duk_get_uint(ctx, 0);
duk_pop(ctx); duk_pop(ctx);
_this->CloseFile(fd); _this->CloseAsyncFile(fd);
return 1; return 1;
} }
@ -1222,6 +1292,12 @@ duk_ret_t CScriptInstance::js_GetRDRAMBlock(duk_context* ctx)
duk_pop_n(ctx, 2); duk_pop_n(ctx, 2);
if (g_MMU == NULL)
{
duk_push_boolean(ctx, false);
return 1;
}
uint8_t* block = (uint8_t*)duk_push_buffer(ctx, size, false); uint8_t* block = (uint8_t*)duk_push_buffer(ctx, size, false);
for (uint32_t i = 0; i < size; i++) for (uint32_t i = 0; i < size; i++)
@ -1431,6 +1507,397 @@ duk_ret_t CScriptInstance::js_ScreenPrint(duk_context* ctx)
return 1; return 1;
} }
duk_ret_t CScriptInstance::js_FSOpen(duk_context* ctx)
{
CScriptInstance* _this = FetchInstance(ctx);
int nargs = duk_get_top(ctx);
if (nargs < 2)
{
duk_pop_n(ctx, nargs);
duk_push_false(ctx);
return 1;
}
const char* path = duk_to_string(ctx, 0);
const char* mode = duk_to_string(ctx, 1);
int fd;
bool res = _this->AddFile(path, mode, &fd);
duk_pop_n(ctx, nargs);
if (res == false)
{
duk_push_false(ctx);
return 1;
}
duk_push_number(ctx, fd);
return 1;
}
duk_ret_t CScriptInstance::js_FSClose(duk_context* ctx)
{
CScriptInstance* _this = FetchInstance(ctx);
int nargs = duk_get_top(ctx);
if (nargs < 1)
{
duk_pop_n(ctx, nargs);
return 1;
}
int fd = duk_get_int(ctx, 0);
duk_pop_n(ctx, nargs);
_this->CloseFile(fd);
return 1;
}
duk_ret_t CScriptInstance::js_FSWrite(duk_context* ctx)
{
CScriptInstance* _this = FetchInstance(ctx);
int nargs = duk_get_top(ctx);
size_t nBytesWritten = 0;
if (nargs < 2)
{
goto end;
}
int fd = duk_get_int(ctx, 0);
FILE* fp = _this->GetFilePointer(fd);
if (fp == NULL)
{
goto end;
}
const char* buffer;
int offset = 0;
duk_size_t length = 0;
int position = 0; // fseek
if (duk_is_string(ctx, 1))
{
// string
const char* str = duk_get_string(ctx, 1);
length = strlen(str);
buffer = str;
}
else
{
// buffer
buffer = (const char*)duk_get_buffer_data(ctx, 1, &length);
if (buffer == NULL)
{
goto end;
}
}
if (nargs > 2 && duk_get_type(ctx, 2) == DUK_TYPE_NUMBER)
{
offset = duk_get_int(ctx, 2);
length -= offset;
}
if (nargs > 3 && duk_get_type(ctx, 3) == DUK_TYPE_NUMBER)
{
size_t lengthArg = duk_get_uint(ctx, 3);
if (lengthArg <= length)
{
length = lengthArg;
}
}
if (nargs > 4 && duk_get_type(ctx, 4) == DUK_TYPE_NUMBER)
{
position = duk_get_int(ctx, 4);
fseek(fp, position, SEEK_SET);
}
nBytesWritten = fwrite(buffer, 1, length, fp);
end:
duk_pop_n(ctx, nargs);
duk_push_number(ctx, nBytesWritten);
return 1;
}
duk_ret_t CScriptInstance::js_FSRead(duk_context* ctx)
{
CScriptInstance* _this = FetchInstance(ctx);
int nargs = duk_get_top(ctx);
size_t nBytesRead = 0;
// (fd, buffer, offset, length, position)
if (nargs < 5)
{
goto end;
}
duk_size_t bufferSize;
int fd = duk_get_int(ctx, 0);
char* buffer = (char*)duk_get_buffer_data(ctx, 1, &bufferSize);
duk_size_t offset = duk_get_uint(ctx, 2);
duk_size_t length = duk_get_uint(ctx, 3);
duk_size_t position = duk_get_uint(ctx, 4);
if (bufferSize == 0)
{
goto end;
}
FILE* fp = _this->GetFilePointer(fd);
if (fp == NULL)
{
goto end;
}
if (offset + length > bufferSize)
{
length = bufferSize - offset;
}
fseek(fp, position, SEEK_SET);
nBytesRead = fread(&buffer[offset], 1, length, fp);
end:
duk_pop_n(ctx, nargs);
duk_push_number(ctx, nBytesRead);
return 1;
}
duk_ret_t CScriptInstance::js_FSFStat(duk_context* ctx)
{
int nargs = duk_get_top(ctx);
if (nargs < 1)
{
duk_pop_n(ctx, nargs);
duk_push_false(ctx);
return 1;
}
int fd = duk_get_int(ctx, 0);
struct stat statbuf;
int res = fstat(fd, &statbuf);
if (res != 0)
{
duk_pop_n(ctx, nargs);
duk_push_false(ctx);
return 1;
}
duk_pop_n(ctx, nargs);
duk_idx_t obj_idx = duk_push_object(ctx);
const duk_number_list_entry props[] = {
{ "dev", (duk_double_t)statbuf.st_dev },
{ "ino", (duk_double_t)statbuf.st_ino },
{ "mode", (duk_double_t)statbuf.st_mode },
{ "nlink", (duk_double_t)statbuf.st_nlink },
{ "uid", (duk_double_t)statbuf.st_uid },
{ "gid", (duk_double_t)statbuf.st_gid },
{ "rdev", (duk_double_t)statbuf.st_rdev },
{ "size", (duk_double_t)statbuf.st_size },
{ "atimeMs", (duk_double_t)statbuf.st_atime * 1000 },
{ "mtimeMs", (duk_double_t)statbuf.st_mtime * 1000 },
{ "ctimeMs", (duk_double_t)statbuf.st_ctime * 1000 },
{ NULL, 0 }
};
duk_put_number_list(ctx, obj_idx, props);
return 1;
}
duk_ret_t CScriptInstance::js_FSStat(duk_context* ctx)
{
int nargs = duk_get_top(ctx);
if (nargs < 1)
{
duk_pop_n(ctx, nargs);
duk_push_false(ctx);
return 1;
}
const char* path = duk_get_string(ctx, 0);
struct stat statbuf;
int res = stat(path, &statbuf);
if (res != 0)
{
duk_pop_n(ctx, nargs);
duk_push_false(ctx);
return 1;
}
duk_pop_n(ctx, nargs);
duk_idx_t obj_idx = duk_push_object(ctx);
const duk_number_list_entry props[] = {
{ "dev", (duk_double_t)statbuf.st_dev },
{ "ino", (duk_double_t)statbuf.st_ino },
{ "mode", (duk_double_t)statbuf.st_mode },
{ "nlink", (duk_double_t)statbuf.st_nlink },
{ "uid", (duk_double_t)statbuf.st_uid },
{ "gid", (duk_double_t)statbuf.st_gid },
{ "rdev", (duk_double_t)statbuf.st_rdev },
{ "size", (duk_double_t)statbuf.st_size },
{ "atimeMs", (duk_double_t)statbuf.st_atime * 1000 },
{ "mtimeMs", (duk_double_t)statbuf.st_mtime * 1000 },
{ "ctimeMs", (duk_double_t)statbuf.st_ctime * 1000 },
{ NULL, 0 }
};
duk_put_number_list(ctx, obj_idx, props);
return 1;
}
duk_ret_t CScriptInstance::js_FSMkDir(duk_context* ctx)
{
int nargs = duk_get_top(ctx);
if (nargs < 1)
{
duk_push_false(ctx);
return 1;
}
const char* path = duk_get_string(ctx, 0);
duk_pop_n(ctx, nargs);
if (CreateDirectory(path, NULL))
{
duk_push_true(ctx);
}
else
{
duk_push_false(ctx);
}
return 1;
}
duk_ret_t CScriptInstance::js_FSRmDir(duk_context* ctx)
{
int nargs = duk_get_top(ctx);
if (nargs < 1)
{
duk_push_false(ctx);
return 1;
}
const char* path = duk_get_string(ctx, 0);
duk_pop_n(ctx, nargs);
if (RemoveDirectory(path))
{
duk_push_true(ctx);
}
else
{
duk_push_false(ctx);
}
return 1;
}
duk_ret_t CScriptInstance::js_FSUnlink(duk_context* ctx)
{
int nargs = duk_get_top(ctx);
if (nargs < 1)
{
duk_push_false(ctx);
return 1;
}
const char* path = duk_get_string(ctx, 0);
duk_pop_n(ctx, nargs);
if (!_unlink(path))
{
duk_push_true(ctx);
}
else
{
duk_push_false(ctx);
}
return 1;
}
duk_ret_t CScriptInstance::js_FSReadDir(duk_context* ctx)
{
int nargs = duk_get_top(ctx);
if (nargs < 1)
{
duk_push_false(ctx);
return 1;
}
const char* path = duk_get_string(ctx, 0);
duk_pop_n(ctx, nargs);
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFile(stdstr_f("%s%s", path, "\\*").c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE)
{
duk_push_false(ctx);
return 1;
}
duk_idx_t arr_idx = duk_push_array(ctx);
int nfile = 0;
do
{
char filename[MAX_PATH];
strcpy(filename, ffd.cFileName);
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
{
continue;
}
duk_push_string(ctx, filename);
duk_put_prop_index(ctx, arr_idx, nfile);
nfile++;
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
return 1;
}
//////////// ////////////
static BOOL ConnectEx(SOCKET s, const SOCKADDR* name, int namelen, PVOID lpSendBuffer, static BOOL ConnectEx(SOCKET s, const SOCKADDR* name, int namelen, PVOID lpSendBuffer,

View File

@ -53,6 +53,13 @@ class CScriptInstance
EVENT_STATUS_ERROR EVENT_STATUS_ERROR
} EVENT_STATUS; } EVENT_STATUS;
// For synchronous file operations
typedef struct
{
FILE* fp;
int fd;
} FILE_FD;
public: public:
CScriptInstance(CDebuggerUI* debugger); CScriptInstance(CDebuggerUI* debugger);
@ -76,10 +83,12 @@ private:
HANDLE m_hIOCompletionPort; HANDLE m_hIOCompletionPort;
CRITICAL_SECTION m_CriticalSection; CRITICAL_SECTION m_CriticalSection;
vector<IOFD> m_Files; vector<IOFD> m_AsyncFiles;
vector<IOLISTENER*> m_Listeners; vector<IOLISTENER*> m_Listeners;
UINT m_NextListenerId; UINT m_NextListenerId;
vector<FILE_FD> m_Files;
CDebuggerUI* m_Debugger; CDebuggerUI* m_Debugger;
CScriptSystem* m_ScriptSystem; CScriptSystem* m_ScriptSystem;
@ -94,13 +103,14 @@ private:
void SetState(INSTANCE_STATE state); void SetState(INSTANCE_STATE state);
void StateChanged(); void StateChanged();
void CleanUp();
void QueueAPC(PAPCFUNC userProc, ULONG_PTR param = 0); void QueueAPC(PAPCFUNC userProc, ULONG_PTR param = 0);
void AddFile(HANDLE fd, bool bSocket = false); void AddAsyncFile(HANDLE fd, bool bSocket = false);
void CloseFile(HANDLE fd); void CloseAsyncFile(HANDLE fd);
void CloseAllFiles(); void CloseAllAsyncFiles();
void RemoveFile(HANDLE fd); void RemoveAsyncFile(HANDLE fd);
HANDLE CreateSocket(); HANDLE CreateSocket();
IOLISTENER* AddListener(HANDLE fd, IOEVENTTYPE evt, void* jsCallback, void* data = NULL, int dataLen = 0); IOLISTENER* AddListener(HANDLE fd, IOEVENTTYPE evt, void* jsCallback, void* data = NULL, int dataLen = 0);
@ -111,6 +121,11 @@ private:
static void CALLBACK EvalAsyncCallback(ULONG_PTR evalWait); static void CALLBACK EvalAsyncCallback(ULONG_PTR evalWait);
bool AddFile(const char* path, const char* mode, int* fd); // return fd
void CloseFile(int fd);
FILE* GetFilePointer(int fd);
void CloseAllFiles();
const char* EvalFile(const char* jsPath); const char* EvalFile(const char* jsPath);
// Lookup list of CScriptInstance instances for static js_* functions // Lookup list of CScriptInstance instances for static js_* functions
@ -159,6 +174,17 @@ private:
static duk_ret_t js_ScreenPrint(duk_context*); // (x, y, text) static duk_ret_t js_ScreenPrint(duk_context*); // (x, y, text)
static duk_ret_t js_FSOpen(duk_context*); // (path, flags) ; returns fd
static duk_ret_t js_FSClose(duk_context*); // (fd)
static duk_ret_t js_FSWrite(duk_context*); // (fd, buffer[, offset[, length[, position]]])
static duk_ret_t js_FSRead(duk_context*); // (fd, buffer, offset, length, position)
static duk_ret_t js_FSFStat(duk_context*); // (fd)
static duk_ret_t js_FSStat(duk_context*); // (path)
static duk_ret_t js_FSMkDir(duk_context*); // (path)
static duk_ret_t js_FSRmDir(duk_context*); // (path)
static duk_ret_t js_FSUnlink(duk_context*); // (path)
static duk_ret_t js_FSReadDir(duk_context*); // (path)
static constexpr duk_function_list_entry NativeFunctions[] = static constexpr duk_function_list_entry NativeFunctions[] =
{ {
{ "addCallback", js_AddCallback, DUK_VARARGS }, { "addCallback", js_AddCallback, DUK_VARARGS },
@ -201,6 +227,17 @@ private:
{ "showCommands", js_ShowCommands, DUK_VARARGS }, { "showCommands", js_ShowCommands, DUK_VARARGS },
{ "screenPrint", js_ScreenPrint, DUK_VARARGS }, { "screenPrint", js_ScreenPrint, DUK_VARARGS },
{ "fsOpen", js_FSOpen, DUK_VARARGS },
{ "fsClose", js_FSClose, DUK_VARARGS },
{ "fsWrite", js_FSWrite, DUK_VARARGS },
{ "fsRead", js_FSRead, DUK_VARARGS },
{ "fsFStat", js_FSFStat, DUK_VARARGS },
{ "fsStat", js_FSStat, DUK_VARARGS },
{ "fsUnlink", js_FSUnlink, DUK_VARARGS },
{ "fsMkDir", js_FSMkDir, DUK_VARARGS },
{ "fsRmDir", js_FSRmDir, DUK_VARARGS },
{ "fsReadDir", js_FSReadDir, DUK_VARARGS },
{ NULL, NULL, 0 } { NULL, NULL, 0 }
}; };
}; };

View File

@ -2,7 +2,8 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Project64d JS API</title> <title>Project64 JS API</title>
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xOdTWsmQAAAOgSURBVDhPZZJ9UNMFGMd/f9Vf3PB4uS3EyxDmTZTXWFIdZEsxmOImTS4Zc3O8jMHaCxsOAhFhgwYTOF7magwGswXuUJcOhgiCuBgQYJyBpFB3HL3cmZ11peA35FZd9bl7/vne9/Pc88dD/MUT/Bpz68ux+UuOKy0Gg5bkjf/Fvfvzwv5h102tVrvVGxHE1YX6F5e/Wy5vGaj/QzmdhKyeA2i0tiw5Hc4kb+VvxtxuQd5AEpov63/qtnanb4Yz38xMyRw8NDxKx0WINufM3UNQtkkwMDRkKqoq8tssbjA8MiIoX2Ch/mEqVBez0GRs0ROWcQM6NyQ7ZPjwi6MwfM9HNyQw/y7cuIaJVptxxel0sp8vGBkbE5y+y4H5mQCa5cOQnVZ4iK4JIzrWc9C1LobIlopLN3tROZkBy1oeOp6IUDLLQlGbHH0ul+3G8I2Sstnj+ORxFjSLaSgolnoIs7sZ1T8no+5RKjItb8Pe02s0tpusZdey8dEPLNT9eBSalVQcaXoL10cGn8lvs6Ff5UA98y5EqnwPcX60Afn3wyF7EIFUQzSufd6nWfp2qVZ/uxJZd6LwwXw0sid3Q1ibsd7aer6PP3gA0jk6eEMRyFbkeojGwVqwxsk45qEgUReGr+7MrZ4b1ILhCkHarWAkOyngVh5bV8gUuaOjozmH7PFgD4WA0bMVQlmOh6jrq0a8g4SEq76IObMdVy47ZnlVJx5HmkiI6fQBX5P5VCKR8KenJ5mW6x8/pLfvBN0WCFqrL07kn/QQOocWsdYgxF14CRHFYbB9ajsr4Ar2KKoK50TVeU8L5YUZU9MTp+qd1Wu79DuwrZGEl5tIoKi2oEApdRO6Xg0SO+hItMQhWhkFV79rUafTxVOpVB8Oh7P/3oMFc6ldgVd1UaDqQxFWF4IwKRWnyksWExISwgntZ2fxWkMc9jbREVkQDavdipJm9Zrtgq1h6uvxidw2LhKr38QbdfHYW0PHPvk7UKmL+slkcsDmd1V0liJEuR2h6lcQyqOivceMCDkNaRVH8F4tE3sU4YhQ7wZNthMpksMQi8V6Go32wqb8HNU5JUjpPvDjbUEQKximLhMC2H4IzPBH4Pv+IHMD4Z/mj+OyzN/4mXyhV/sHvlAoOShM+YWUTEIQMxg12pohcbFklZJCgW/KxuKDAcgpFK2wmKzXvcr/Ye5n7hDknxyOZMdCo9FoGQzGttKKMncCdx8kKqkndldssLf6HwjiT/PF9/HEYv4PAAAAAElFTkSuQmCC"/>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,400italic,700,700italic' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,400italic,700,700italic' rel='stylesheet' type='text/css'>
<style> <style>
body { body {
@ -90,7 +91,7 @@ span.tag2 {
</style> </style>
</head> </head>
<body> <body>
<div style="margin: 10px; font-size: 24px;">Project64d Javascript API</div> <div style="margin: 10px; font-size: 24px;">Project64 Javascript API</div>
<div class="inline-content" style="width: 150px; min-width: 150px; margin-left: 10px;"> <div class="inline-content" style="width: 150px; min-width: 150px; margin-left: 10px;">
<div class="panel" id="sidebar" style="width: 120px; position: absolute;"> <div class="panel" id="sidebar" style="width: 120px; position: absolute;">
<a href="#mem">mem</a><br> <a href="#mem">mem</a><br>
@ -98,6 +99,8 @@ span.tag2 {
<a href="#events">events</a><br> <a href="#events">events</a><br>
<a href="#debug">debug</a><br> <a href="#debug">debug</a><br>
<a href="#console">console</a><br> <a href="#console">console</a><br>
<a href="#fs">fs</a><br>
<a href="#fs.Stats">fs.Stats</a><br>
<a href="#alert">alert</a><br> <a href="#alert">alert</a><br>
<a href="#screen">screen</a><br> <a href="#screen">screen</a><br>
<a href="#gpr">gpr</a><br> <a href="#gpr">gpr</a><br>
@ -388,6 +391,141 @@ events.onexec(0x802CB1C0, function()
</div> </div>
</div> </div>
<div class="panel" id="fs">
<div class="classname">fs</div>
<div class="property">
<div class="propertyname">fs.open(path, mode)</div>
<div class="propertydesc">
Opens the file pointed to by <span class="snip">path</span> in the mode specified by <span class="snip">mode</span>.
Returns a file descriptor on success or <span class="snip">false</span> if the operation failed.
<pre class="example">var fd = fs.open('log.txt', 'w')</pre>
Valid modes:
<pre>
r or rb Open file for reading.
w or wb Truncate to zero length or create file for writing.
a or ab Append; open or create file for writing at end-of-file.
r+ or rb+ or r+b Open file for update (reading and writing).
w+ or wb+ or w+b Truncate to zero length or create file for update.
a+ or ab+ or a+b Append; open or create file for update, writing at end-of-file.
<a href="http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html">http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html</a>
</pre>
</div>
</div>
<div class="property">
<div class="propertyname">fs.close(fd)</div>
<div class="propertydesc">
Closes the file referenced by <span class="snip">fd</span>.
</div>
</div>
<div class="property">
<div class="propertyname">fs.write(fd, buffer[, offset[, length[, position]]])</div>
<div class="propertydesc">
Writes <span class="snip">buffer</span> to the file referenced by <span class="snip">fd</span>. Returns the number of bytes written.
<pre class="example">var fd = fs.open('log.txt', 'w')
fs.write(fd, 'Hello world!\n')
fs.close(fd)</pre>
<pre class="example">var buf = mem.getblock(0x8033B400, 4 * 32)
var fd = fs.open('data.bin', 'wb')
fs.write(fd, buf)
fs.close(fd)</pre>
</div>
</div>
<div class="property">
<div class="propertyname">fs.writeFile(path, buffer)</div>
<div class="propertydesc">
Writes <span class="snip">buffer</span> to the file pointed to by <span class="snip">path</span>.
Returns <span class="snip">true</span> if the operation was successful or <span class="snip">false</span> if the operation failed.
</div>
</div>
<div class="property">
<div class="propertyname">fs.read(fd, buffer, offset, length, position)</div>
<div class="propertydesc">
Reads the file referenced by <span class="snip">fd</span> into <span class="snip">buffer</span>. Returns the number of bytes read.
<pre class="example">var buf = new Buffer(128)
var fd = fs.open('data.bin', 'rb')
var nBytesRead = fs.read(fd, buf, 0, buf.length, 0)
fs.close(fd)</pre>
</div>
</div>
<div class="property">
<div class="propertyname">fs.readFile(path)</div>
<div class="propertydesc">
Reads the file pointed to by <span class="snip">path</span>. Returns a Buffer object representing the file, or <span class="snip">false</span> if the operation failed.
</div>
</div>
<div class="property">
<div class="propertyname">fs.fstat(fd)</div>
<div class="propertydesc">
Returns an <a href="#fs.Stats">fs.Stats</a> object describing the file referenced by <span class="snip">fd</span>.
<pre class="example">var stats = fs.fstat(fd)
console.log('size: ' + stats.size)
</pre>
</div>
</div>
<div class="property">
<div class="propertyname">fs.stat(path)</div>
<div class="propertydesc">
Returns an <a href="#fs.Stats">fs.Stats</a> object describing the file or directory pointed to by <span class="snip">path</span>. Returns <span class="snip">false</span> if the operation failed..
</div>
</div>
<div class="property">
<div class="propertyname">fs.unlink(path)</div>
<div class="propertydesc">Deletes a file. Returns <span class="snip">true</span> if the operation was successful.</div>
</div>
<div class="property">
<div class="propertyname">fs.mkdir(path)</div>
<div class="propertydesc">Creates a directory. Returns <span class="snip">true</span> if the operation was successful.</div>
</div>
<div class="property">
<div class="propertyname">fs.rmdir(path)</div>
<div class="propertydesc">Removes a directory. The directory must be empty. Returns <span class="snip">true</span> if the operation was successful.</div>
</div>
<div class="property">
<div class="propertyname">fs.readdir(path)</div>
<div class="propertydesc">
Returns an array of file names in the directory pointed to by <span class="snip">path</span>.
Returns <span class="snip">false</span> if the operation failed.
</div>
</div>
</div>
<div class="panel" id="fs.Stats">
<div class="classname">fs.Stats</div>
<div class="propertydesc">
<table>
<tr><td><b>stats.dev</b> </td><td> ID of the device the file resides on</td></tr>
<tr><td><b>stats.ino</b> </td><td> inode number</td></tr>
<tr><td><b>stats.mode</b> </td><td> File permissions</td></tr>
<tr><td><b>stats.nlink</b> </td><td> Number of links to the file</td></tr>
<tr><td><b>stats.uid</b> </td><td> User ID</td></tr>
<tr><td><b>stats.gid</b> </td><td> Group ID</td></tr>
<tr><td><b>stats.rdev</b> </td><td> Device ID (if file is character or block special)</td></tr>
<tr><td><b>stats.size</b> </td><td> Size of the file in bytes</td></tr>
<tr><td><b>stats.atimeMs</b> </td><td> Last access timestamp in milliseconds</td></tr>
<tr><td><b>stats.mtimeMs</b> </td><td> Last modification timestamp in milliseconds</td></tr>
<tr><td><b>stats.ctimeMs</b> </td><td> Creation timestamp in milliseconds</td></tr>
<tr><td><b>stats.atime</b> </td><td> JS Date object representing the last access time</td></tr>
<tr><td><b>stats.mtime</b> </td><td> JS Date object representing the last modification time</td></tr>
<tr><td><b>stats.ctime</b> </td><td> JS Date object representing the creation time</td></tr>
</table>
</div>
<div class="property">
<div class="propertyname">stats.isDirectory()</div>
<div class="propertydesc">
Returns <span class="snip">true</span> if the <a href="#fs.Stats">fs.Stats</a> object describes a directory.
</div>
</div>
<div class="property">
<div class="propertyname">stats.isFile()</div>
<div class="propertydesc">
Returns <span class="snip">true</span> if the <a href="#fs.Stats">fs.Stats</a> object describes a regular file.
</div>
</div>
</div>
<div class="panel" id="console"> <div class="panel" id="console">
<div class="classname">console</div> <div class="classname">console</div>
<div class="property"> <div class="property">
@ -441,7 +579,7 @@ events.onexec(0x80000180, function()
<pre class="example"> <pre class="example">
events.ondraw(function() events.ondraw(function()
{ {
screen.print(20, 20, "power: ' + mem.u8[0x8033B21E]) screen.print(20, 20, 'power: ' + mem.u8[0x8033B21E])
})</pre> })</pre>
</div> </div>
</div> </div>
@ -646,7 +784,7 @@ server.on('connection', function(socket)
Returns a hexadecimal string representation of the number object. The resulting string is prepended with zeroes until its character length meets <span class="snip">nChars</span> or 8 by default. Returns a hexadecimal string representation of the number object. The resulting string is prepended with zeroes until its character length meets <span class="snip">nChars</span> or 8 by default.
<pre class="example"> <pre class="example">
var sm64EntryPC = rom.u32[0x00000008] var sm64EntryPC = rom.u32[0x00000008]
console.log("Entry: " + sm64EntryPC.hex()) // "Entry: 80246000"</pre> console.log('Entry: ' + sm64EntryPC.hex()) // "Entry: 80246000"</pre>
</div> </div>
</div> </div>
</div> </div>