diff --git a/Makefile b/Makefile index 606550f..d7bd5ff 100644 --- a/Makefile +++ b/Makefile @@ -25,12 +25,14 @@ include $(DEVKITARM)/3ds_rules # - .png # - icon.png # - /default_icon.png +# ROMFS: if set, use the files at this path to build a ROMFS #--------------------------------------------------------------------------------- TARGET := ctruLua BUILD := build SOURCES := source libs/lua-5.3.1/src DATA := data INCLUDES := include libs/lua-5.3.1/src libs/lzlib +#ROMFS := romfs APP_TITLE := ctruLua APP_DESCRIPTION := Lua for the 3DS. Yes, it works. @@ -125,6 +127,11 @@ ifeq ($(strip $(NO_SMDH)),) export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh endif +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) + CFLAGS += -DROMFS +endif + .PHONY: $(BUILD) clean all #--------------------------------------------------------------------------------- diff --git a/source/ctr.c b/source/ctr.c index 2359bcb..b046636 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -77,6 +77,13 @@ The `ctr.cfgu` module. */ void load_cfgu_lib(lua_State *L); +/*** +The `ctr.socket` module. +@table socket +@see ctr.socket +*/ +void load_socket_lib(lua_State *L); + //void load_cam_lib(lua_State *L); /*** @@ -110,15 +117,16 @@ static const struct luaL_Reg ctr_lib[] = { // Subtables struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } ctr_libs[] = { - { "gfx", load_gfx_lib, unload_gfx_lib }, - { "news", load_news_lib, NULL }, - { "ptm", load_ptm_lib, NULL }, - { "hid", load_hid_lib, unload_hid_lib }, - { "ir", load_ir_lib, NULL }, - { "fs", load_fs_lib, unload_fs_lib }, - { "httpc", load_httpc_lib, unload_httpc_lib }, - { "qtm", load_qtm_lib, NULL }, - { "cfgu", load_cfgu_lib, NULL }, + { "gfx", load_gfx_lib, unload_gfx_lib }, + { "news", load_news_lib, NULL }, + { "ptm", load_ptm_lib, NULL }, + { "hid", load_hid_lib, unload_hid_lib }, + { "ir", load_ir_lib, NULL }, + { "fs", load_fs_lib, unload_fs_lib }, + { "httpc", load_httpc_lib, unload_httpc_lib }, + { "qtm", load_qtm_lib, NULL }, + { "cfgu", load_cfgu_lib, NULL }, + { "socket", load_socket_lib, NULL }, // { "cam", load_cam_lib, NULL }, { NULL, NULL } }; diff --git a/source/fs.c b/source/fs.c index 940ef58..576ec48 100644 --- a/source/fs.c +++ b/source/fs.c @@ -9,6 +9,9 @@ Handle *fsuHandle; FS_archive sdmcArchive; +#ifdef ROMFS +FS_archive romfsArchive; +#endif void load_lzlib(lua_State *L); @@ -126,12 +129,19 @@ void load_fs_lib(lua_State *L) { sdmcArchive = (FS_archive){ARCH_SDMC, FS_makePath(PATH_EMPTY, "")}; FSUSER_OpenArchive(fsuHandle, &sdmcArchive); + #ifdef ROMFS + romfsArchive = (FS_archive){ARCH_ROMFS, FS_makePath(PATH_EMPTY, "")}; + FSUSER_OpenArchive(fsuHandle, &romfsArchive); + #endif luaL_requiref(L, "ctr.fs", luaopen_fs_lib, false); } void unload_fs_lib(lua_State *L) { FSUSER_CloseArchive(fsuHandle, &sdmcArchive); + #ifdef ROMFS + FSUSER_CloseArchive(fsuHandle, &romfsArchive); + #endif fsExit(); } diff --git a/source/gfx.c b/source/gfx.c index 821fe02..c43be37 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -9,6 +9,7 @@ The `gfx` module. #include #include <3ds/vram.h> +#include <3ds/services/gsp.h> #include #include @@ -134,6 +135,22 @@ static int gfx_setVBlankWait(lua_State *L) { return 0; } +/*** +Wait for the VBlank interruption. +@function waitForVBlank +@tparam[opt=GFX_TOP] number screen the screen's VBlank to wait for +*/ +static int gfx_waitForVBlank(lua_State *L) { + u8 screen = luaL_optinteger(L, 1, GFX_TOP); + if (screen == GFX_TOP) { + gspWaitForVBlank0(); + } else { + gspWaitForVBlank1(); + } + + return 0; +} + /*** Get free VRAM space. @function vramSpaceFree @@ -348,6 +365,7 @@ static const struct luaL_Reg gfx_lib[] = { { "set3D", gfx_set3D }, { "get3D", gfx_get3D }, { "setVBlankWait", gfx_setVBlankWait }, + { "waitForVBlank", gfx_waitForVBlank }, { "vramSpaceFree", gfx_vramSpaceFree }, { "line", gfx_line }, { "point", gfx_point }, diff --git a/source/ir.c b/source/ir.c index c58333c..705091b 100644 --- a/source/ir.c +++ b/source/ir.c @@ -103,6 +103,7 @@ Receive some data from the IR module. @function receive @tparam[opt=buffer size] number size bytes to receive @tparam[opt=false] boolean wait wait until the data is received +@return string data */ static int ir_receive(lua_State *L) { u32 size = luaL_optinteger(L, 1, bufferSize); diff --git a/source/news.c b/source/news.c index 18932d6..20f78d8 100644 --- a/source/news.c +++ b/source/news.c @@ -33,17 +33,13 @@ Send a notification to the user. WIP, do not use !!! static int news_notification(lua_State *L) { const char *title = luaL_checkstring(L, 1); const char *message = luaL_checkstring(L, 2); - const void *imageData = luaL_optstring(L, 3, NULL); + + u32 imageDataLength = 0; + const void *imageData = luaL_optlstring(L, 3, NULL, (size_t*)&imageDataLength); bool jpeg = false; if (lua_isboolean(L, 4)) jpeg = lua_toboolean(L, 4); - u32 imageDataLength = 0; - if (imageData) { - lua_len(L, 3); - luaL_checkinteger(L, -1); - } - const u16* cTitle = 0; const u16* cMessage = 0; u32 titleLength, messageLength; diff --git a/source/socket.c b/source/socket.c new file mode 100644 index 0000000..173e0a0 --- /dev/null +++ b/source/socket.c @@ -0,0 +1,166 @@ +/*** +The `socket` module. Almost like luasocket, but for TCP/UDP only. +See http://w3.impa.br/~diego/software/luasocket/reference.html for a +documentation. +@module ctr.socket +@usage local socket = require("ctr.socket") +*/ + +#include <3ds.h> +#include <3ds/types.h> +#include <3ds/services/soc.h> + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + int socket; + struct sockaddr_in addr; + struct hostent *host; // only user for client sockets +} socket_userdata; + +static int socket_init(lua_State *L) { + u32 size = luaL_optinteger(L, 1, 0x10000); + Result ret = SOC_Initialize((u32*)memalign(0x1000, size), size); + + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + +static int socket_shutdown(lua_State *L) { + SOC_Shutdown(); + + return 0; +} + +static int socket_tcp(lua_State *L) { + socket_userdata *data = lua_newuserdata(L, sizeof(*data)); + luaL_getmetatable(L, "LSocket"); + lua_setmetatable(L, -2); + + data->socket = socket(AF_INET, SOCK_STREAM, 0); + if (data->socket < 0) { + lua_pushnil(L); + lua_pushstring(L, "Failed to create a TCP socket"); + return 2; + } + + data->addr.sin_family = AF_INET; + + return 1; +} + +/* methods */ + +static int socket_close(lua_State *L) { + socket_userdata *data = luaL_checkudata(L, 1, "LSocket"); + + close(data->socket); + + return 0; +} + +static int socket_connect(lua_State *L) { + socket_userdata *data = luaL_checkudata(L, 1, "LSocket"); + char *addr = (char*)luaL_checkstring(L, 2); + int port = luaL_checkinteger(L, 3); + + data->host = gethostbyname(addr); + if (data->host == NULL) { + lua_pushnil(L); + lua_pushstring(L, "No such host"); + return 2; + } + + data->addr.sin_port = htons(port); + bcopy((char*)data->host->h_addr, (char*)&data->addr.sin_addr.s_addr, data->host->h_length); + + if (connect(data->socket, (const struct sockaddr*)&data->addr, sizeof(data->addr)) < 0) { + lua_pushnil(L); + lua_pushstring(L, "Connection failed"); + return 2; + } + + lua_pushinteger(L, 1); + return 1; +} + +static int socket_receive(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + int count = 0; + int flags = 0; + if (lua_isnumber(L, 2)) { + count = luaL_checkinteger(L, 2); + } else if (lua_isstring(L, 2) && luaL_checkstring(L, 2) == (char*)&"*a") { + count = SIZE_MAX/2; + } else { + lua_pushnil(L); + lua_pushstring(L, ""); + return 2; + } + + char *buff = malloc(count); + recv(userdata->socket, buff, count, flags); + + lua_pushstring(L, buff); + return 1; +} + +static int socket_send(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + size_t size = 0; + char *data = (char*)luaL_checklstring(L, 2, &size); + + size_t sent = send(userdata->socket, data, size, 0); + + lua_pushinteger(L, sent); + return 1; +} + +// module functions +static const struct luaL_Reg socket_functions[] = { + {"init", socket_init }, + {"shutdown", socket_shutdown}, + {"tcp", socket_tcp }, + {NULL, NULL} +}; + +// object +static const struct luaL_Reg socket_methods[] = { + {"close", socket_close }, + {"connect", socket_connect }, + {"receive", socket_receive }, + {"send", socket_send }, + {NULL, NULL} +}; + +int luaopen_socket_lib(lua_State *L) { + luaL_newmetatable(L, "LSocket"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, socket_methods, 0); + + luaL_newlib(L, socket_functions); + + return 1; +} + +void load_socket_lib(lua_State *L) { + luaL_requiref(L, "ctr.socket", luaopen_socket_lib, false); +}