mirror of
https://github.com/ctruLua/ctruLua.git
synced 2025-10-28 08:49:30 +00:00
Compare commits
No commits in common. "master" and "v1.0-beta3" have entirely different histories.
master
...
v1.0-beta3
59 changed files with 966 additions and 11546 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,3 @@
|
||||||
/build/*
|
/build/*
|
||||||
/ctruLua.*
|
/ctruLua.*
|
||||||
/doc/html/*
|
/doc/html/*
|
||||||
/doc/sublimetext/*
|
|
||||||
|
|
|
||||||
29
Makefile
29
Makefile
|
|
@ -38,8 +38,6 @@ APP_TITLE := ctruLua
|
||||||
APP_DESCRIPTION := Lua for the 3DS. Yes, it works.
|
APP_DESCRIPTION := Lua for the 3DS. Yes, it works.
|
||||||
APP_AUTHOR := Reuh, Firew0lf and NegiAD
|
APP_AUTHOR := Reuh, Firew0lf and NegiAD
|
||||||
ICON := icon.png
|
ICON := icon.png
|
||||||
APP_VERSION := $(shell git describe --abbrev=0 --tags)
|
|
||||||
LASTCOMMIT := $(shell git rev-parse HEAD)
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
|
|
@ -50,10 +48,7 @@ CFLAGS := -g -Wall -O2 -mword-relocations -std=gnu11 \
|
||||||
-fomit-frame-pointer -ffast-math \
|
-fomit-frame-pointer -ffast-math \
|
||||||
$(ARCH)
|
$(ARCH)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DCTR_VERSION=\"$(APP_VERSION)\" -DCTR_BUILD=\"$(LASTCOMMIT)\"
|
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
|
||||||
ifneq ($(ROMFS),)
|
|
||||||
CFLAGS += -DROMFS
|
|
||||||
endif
|
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||||
|
|
||||||
|
|
@ -70,8 +65,7 @@ LIBDIRS := $(CTRULIB) $(PORTLIBS) \
|
||||||
$(CURDIR)/libs/3ds_portlibs/build \
|
$(CURDIR)/libs/3ds_portlibs/build \
|
||||||
$(CURDIR)/libs/sf2dlib/libsf2d \
|
$(CURDIR)/libs/sf2dlib/libsf2d \
|
||||||
$(CURDIR)/libs/sftdlib/libsftd \
|
$(CURDIR)/libs/sftdlib/libsftd \
|
||||||
$(CURDIR)/libs/sfillib/libsfil \
|
$(CURDIR)/libs/sfillib/libsfil
|
||||||
$(CURDIR)/libs/stb
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# no real need to edit anything past this point unless you need to add additional
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
|
@ -135,6 +129,7 @@ endif
|
||||||
|
|
||||||
ifneq ($(ROMFS),)
|
ifneq ($(ROMFS),)
|
||||||
export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS)
|
export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS)
|
||||||
|
CFLAGS += -DROMFS
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: $(BUILD) clean all
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
@ -171,17 +166,8 @@ build-all:
|
||||||
@make build
|
@make build
|
||||||
|
|
||||||
build-doc:
|
build-doc:
|
||||||
@echo Building HTML documentation...
|
|
||||||
@make build-doc-html
|
|
||||||
@echo Building SublimeText documentation...
|
|
||||||
@make build-doc-st
|
|
||||||
|
|
||||||
build-doc-html:
|
|
||||||
@cd doc/ && ldoc . && cd ..
|
@cd doc/ && ldoc . && cd ..
|
||||||
|
|
||||||
build-doc-st:
|
|
||||||
@cd doc/ && ldoc . --template ./ --ext sublime-completions --dir ./sublimetext/ && cd ..
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
clean:
|
clean:
|
||||||
@rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf
|
@rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf
|
||||||
|
|
@ -211,17 +197,8 @@ clean-all:
|
||||||
@make clean
|
@make clean
|
||||||
|
|
||||||
clean-doc:
|
clean-doc:
|
||||||
@echo Cleaning HTML documentation...
|
|
||||||
@make clean-doc-html
|
|
||||||
@echo Cleaning SublimeText documentation...
|
|
||||||
@make clean-doc-st
|
|
||||||
|
|
||||||
clean-doc-html:
|
|
||||||
@rm -rf doc/html
|
@rm -rf doc/html
|
||||||
|
|
||||||
clean-doc-st:
|
|
||||||
@rm -rf doc/sublimetext
|
|
||||||
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
else
|
else
|
||||||
|
|
|
||||||
81
README.md
81
README.md
|
|
@ -1,86 +1,25 @@
|
||||||
# ctrµLua
|
# ctrµLua
|
||||||

|
|
||||||
|
Everything is in the Wiki.
|
||||||
|
|
||||||
Warning: the 'u' in the repo's name is a 'µ', not a 'u'.
|
Warning: the 'u' in the repo's name is a 'µ', not a 'u'.
|
||||||
|
|
||||||
### Users part
|
#### Builds 
|
||||||
|
|
||||||
#### How to install
|
* Most recent working build: [here](http://thomas99.no-ip.org:3000/ctrulua/builds/latest/artifacts/ctruLua.3dsx) (warning: only tested with Citra, sometimes on real hardware).
|
||||||
|
* See http://thomas99.no-ip.org:3000/ctrulua for all the builds.
|
||||||
* Download ctruLua.zip from the [releases](https://github.com/ctruLua/ctruLua/releases) (for something stable) or the [CI server](https://reuh.eu/ctrulua/ci/ctrulua) (for more features)
|
|
||||||
* Unzip it on your SD card, in a folder inside your homebrews folder; if you use HBL, extract it in `/3ds/ctrulua/`
|
|
||||||
* Put some scripts wherever you want, just remember where
|
|
||||||
* Launch CtrµLua from your homebrew launcher
|
|
||||||
* Use the shell to run your scripts
|
|
||||||
|
|
||||||
### Homebrewers part
|
|
||||||
|
|
||||||
#### Builds 
|
|
||||||
|
|
||||||
* Most recent working build: [ctruLua.3dsx](https://reuh.eu/ctrulua/ci/ctrulua/builds/latest/artifacts/ctruLua.3dsx)
|
|
||||||
* See [https://reuh.eu/ctrulua/ci/ctrulua](https://reuh.eu/ctrulua/ci/ctrulua) for all the builds.
|
|
||||||
|
|
||||||
#### Hello world
|
|
||||||
|
|
||||||
```Lua
|
|
||||||
local ctr = require("ctr")
|
|
||||||
local gfx = require("ctr.gfx")
|
|
||||||
local hid = require("ctr.hid")
|
|
||||||
|
|
||||||
while ctr.run() do
|
|
||||||
hid.read()
|
|
||||||
local keys = hid.keys()
|
|
||||||
if keys.held.start then break end
|
|
||||||
|
|
||||||
gfx.start(gfx.TOP)
|
|
||||||
gfx.text(2, 2, "Hello, world !")
|
|
||||||
gfx.stop()
|
|
||||||
|
|
||||||
gfx.render()
|
|
||||||
end
|
|
||||||
```
|
|
||||||
This script will print "Hello, world !" on the top screen, and will exit if the user presses Start.
|
|
||||||
This is the "graphical" version; there's also a text-only version, based on the console:
|
|
||||||
```Lua
|
|
||||||
local ctr = require("ctr")
|
|
||||||
local gfx = require("ctr.gfx")
|
|
||||||
local hid = require("ctr.hid")
|
|
||||||
|
|
||||||
gfx.console()
|
|
||||||
print("Hello, world !")
|
|
||||||
|
|
||||||
while ctr.run() do
|
|
||||||
hid.read()
|
|
||||||
local keys = hid.keys()
|
|
||||||
if keys.held.start then break end
|
|
||||||
|
|
||||||
gfx.render()
|
|
||||||
end
|
|
||||||
|
|
||||||
gfx.disableConsole()
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Lua API Documentation
|
|
||||||
|
|
||||||
* An online version of the documentation can be found [here](https://reuh.eu/ctrulua/latest/html/)
|
|
||||||
* To build the documentation, run `make build-doc-html` (requires [LDoc](https://github.com/stevedonovan/LDoc)).
|
|
||||||
|
|
||||||
### Developers part
|
|
||||||
|
|
||||||
#### Build instructions
|
#### Build instructions
|
||||||
|
|
||||||
* Setup your environment as shown here : http://3dbrew.org/wiki/Setting_up_Development_Environment
|
* Setup your environment as shown here : http://3dbrew.org/wiki/Setting_up_Development_Environment
|
||||||
* Clone this repository and run the command `make build-all` to build all the dependencies.
|
* Clone this repository and run the command `make build-all` to build all the dependencies.
|
||||||
* If you only made changes to ctrµLua, run `make` to rebuild ctrµLua without rebuilding all the dependencies.
|
* If you only made changes to ctrµLua, run `make` to rebuild ctµLua without rebuilding all the dependencies.
|
||||||
|
|
||||||
May not work under Windows.
|
May not work under Windows.
|
||||||
|
|
||||||
### Credits
|
#### Lua API Documentation
|
||||||
|
|
||||||
* __Smealum__ and everyone who worked on the ctrulib: [https://github.com/smealum/ctrulib](https://github.com/smealum/ctrulib)
|
* An online version of the documentation can be found here : http://thomas99.no-ip.org/ctrulua
|
||||||
* __Xerpi__ for the [sf2dlib](https://github.com/xerpi/sf2dlib), [sftdlib](https://github.com/xerpi/sftdlib) and [sfillib](https://github.com/xerpi/sfillib)
|
* To build the documentation, run `make build-doc` (requires [LDoc](https://github.com/stevedonovan/LDoc)).
|
||||||
* __All the [Citra](https://citra-emu.org/) developers__
|
|
||||||
* __Everyone who worked on [DevKitARM](http://devkitpro.org/)__
|
|
||||||
* __Nothings__ for the [stb](https://github.com/nothings/stb) libs
|
|
||||||
* Everyone who worked on the other libs we use
|
|
||||||
|
|
||||||
|
#### Based on ctrulib by smealum: [https://github.com/smealum/ctrulib](https://github.com/smealum/ctrulib)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ format = "markdown"
|
||||||
plain = true
|
plain = true
|
||||||
|
|
||||||
-- Input files
|
-- Input files
|
||||||
topics = {"../README.md", "filepicker.md"}
|
topics = "../README.md"
|
||||||
file = "../source/"
|
file = "../source/"
|
||||||
examples = "../sdcard/3ds/ctruLua/examples/"
|
examples = "../sdcard/3ds/ctruLua/examples/"
|
||||||
manual_url = "file://../libs/lua-5.3.1/doc/manual.html"
|
manual_url = "file://../libs/lua-5.3.1/doc/manual.html"
|
||||||
|
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
# filepicker
|
|
||||||
## filePicker([workingDirectory[, bindings[, callbacks[, ...]]]])
|
|
||||||
### Argument: workingDirectory
|
|
||||||
The directory that shows up first in the file browser.
|
|
||||||
If this is nil, ctr.fs.getDirectory() is used.
|
|
||||||
The recommended form is sdmc:/path/ or romfs:/path/, but it can be a simple /path/ instead.
|
|
||||||
#### Possible values
|
|
||||||
- string
|
|
||||||
- nil
|
|
||||||
|
|
||||||
### Argument: bindings
|
|
||||||
A table, list of filetypes and key bindings related to these filetypes.
|
|
||||||
#### Format
|
|
||||||
```
|
|
||||||
{
|
|
||||||
__default, __directory, [Lua regexp] = {
|
|
||||||
[keys from ctr.hid.keys()] = {
|
|
||||||
function
|
|
||||||
string
|
|
||||||
}...
|
|
||||||
__name = string
|
|
||||||
}...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The Lua regexp is matched against the filename to determine if it is of this type.
|
|
||||||
__directory is the "file type" for directories
|
|
||||||
__default is the "file type" for files that cannot be matched with any other.
|
|
||||||
Also, every other type inherits the values it doesn't define from __default.
|
|
||||||
A file type contains the human-readable name (__name) of the type, displayed on the bottom screen,
|
|
||||||
as well as an optional binding for each key.
|
|
||||||
The optional binding is formed of an anonymous function, followed by the key's label to be displayed on the bottom screen,
|
|
||||||
if the label is nil, the key isn't displayed but still bound.
|
|
||||||
|
|
||||||
The function is defined as-is:
|
|
||||||
##### function(externalConfig, selected, bindingPattern, bindingKey)
|
|
||||||
externalConfig.workingDirectory is the active directory for filePicker, doesn't necessarily match ctrµLua's.
|
|
||||||
externalConfig.bindings, externalConfig.callbacks and externalConfig.additionalArguments all are the arguments passed to filePicker,
|
|
||||||
starting from position 2.
|
|
||||||
externalConfig.fileList is the list of files currently displayed by filePicker, in the same format as is returned by ctr.fs.listDirectory().
|
|
||||||
selected.inList is the absolute position of the cursor in externalConfig.fileList
|
|
||||||
selected.offset is the number of items skipped for display from fileList
|
|
||||||
bindingPattern is the [Lua regexp] defined earlier, and bindingKey the [key] that triggered this event.
|
|
||||||
This function may return the same thing as filePicker itself (Defined later here), or nothing. If it returns nothing,
|
|
||||||
filePicker will keep running, if it returns the same returns as filePicker, filePicker will exit, returning these values.
|
|
||||||
|
|
||||||
#### Notes
|
|
||||||
Sane defaults are set if you did not set them otherwise:
|
|
||||||
__default.x quits filePicker, returning the current directory, "__directory", nil and "x". See the returns to understand what this means.
|
|
||||||
__directory.a changes directories to that directory.
|
|
||||||
|
|
||||||
### Argument: callbacks
|
|
||||||
A table defining the callbacks ran at the end of each of the equivalent phases.
|
|
||||||
#### Format
|
|
||||||
```
|
|
||||||
{
|
|
||||||
drawTop, drawBottom, eventHandler = function
|
|
||||||
}
|
|
||||||
```
|
|
||||||
All of these take the following parameters: (externalConfig, selected)
|
|
||||||
They have the meaning defined earlier.
|
|
||||||
|
|
||||||
#### Notes
|
|
||||||
Although drawTop and drawBottom are ran at the end of their respective functions,
|
|
||||||
eventHandler is not, as it cannot be without being run repeatedly, so it's run at the beginning of
|
|
||||||
the ACTUAL eventHandler instead of its end.
|
|
||||||
|
|
||||||
### Argument: ...
|
|
||||||
Additional parameters. All of these are aggregated orderly in externalConfig.additionalArguments and passed around as explained earlier.
|
|
||||||
They have no specific meaning unless defined so by event handling functions.
|
|
||||||
|
|
||||||
### Return 1: selectedPath
|
|
||||||
The path selected by the either, may or may not have the sdmc:/romfs: prefix, depending on your input.
|
|
||||||
A string.
|
|
||||||
|
|
||||||
### Return 2: bindingPattern
|
|
||||||
The pattern the file matched to. You can use this to know exactly which kind of file you're dealing with
|
|
||||||
A string.
|
|
||||||
|
|
||||||
### Return 3: mode
|
|
||||||
Included handlers may have it be "open", in case you're opening an existing file, "new" in case the user wants to create a new file,
|
|
||||||
Or nil. A "nil" is assumed to mean that the user didn't pick anything.
|
|
||||||
A string or nil.
|
|
||||||
|
|
||||||
### Return 4: key
|
|
||||||
The key that triggered the event that made filePicker exit.
|
|
||||||
A string.
|
|
||||||
|
|
||||||
## Included event handlers you have available are:
|
|
||||||
- filepicker.changeDirectory - The name is on the tin, change workingDirectory to the active element and refresh the file list for that path.
|
|
||||||
- filepicker.openFile - Quits and returns as described by this document based on the active element.
|
|
||||||
- filepicker.newFile - Prompts the user to input a file name manually, relative to the current working directory, and with FAT-incompatible characters excluded. Quits and returns that.
|
|
||||||
- filepicker.nothing - Do nothing and keep on running. Literally. This is used as a plug to enable an action for all (or a certain type) but files of a certain type (or more precise than the initial type).
|
|
||||||
118
doc/ldoc.ltp
118
doc/ldoc.ltp
|
|
@ -1,118 +0,0 @@
|
||||||
# -- LDoc template by Reuh.
|
|
||||||
# -- Generates sublime-completions files which can be used in Sublime Text 3 for autocompletion.
|
|
||||||
# -- Based on the HTML template, so generated files will contain lots of comments with additionnal data not handled by ST.
|
|
||||||
# -- I tried to make the generated files human-readable, so they may be used instead of the HTML documentation.
|
|
||||||
# -- Typical usage: ldoc . --template ./ --ext sublime-completions --dir ./sublimetext/
|
|
||||||
#
|
|
||||||
# local scope = "source.lua"
|
|
||||||
# local function e(str) return (str or ""):gsub("\"", "\\\"") end -- escape json string ("str")
|
|
||||||
# local function indent(indentation, str) -- indent str (except first line) with indentation
|
|
||||||
# return (str or ""):gsub("(.-)\n", indentation.."%1\n"):gsub("\n([^\n]*)$", "\n"..indentation.."%1"):gsub("^"..indentation, "")
|
|
||||||
# end
|
|
||||||
# local function displayName(item) return item.type == "function" and item.name..item.args or item.name end -- nice name
|
|
||||||
# local function autocompleteName(item) -- ST-autocomplete name
|
|
||||||
# if item.type == "function" then
|
|
||||||
# local i = 1
|
|
||||||
# local args = "("
|
|
||||||
# for arg in (item.args:match("^%((.*)%)$")..","):gmatch("%s*([^,]+)%,") do
|
|
||||||
# args = args.."${"..i..":"..arg.."}, "
|
|
||||||
# i = i +1
|
|
||||||
# end
|
|
||||||
# return item.name..args:gsub("%, $", "")..")"
|
|
||||||
# else return item.name end
|
|
||||||
# end
|
|
||||||
/*
|
|
||||||
Title: $(ldoc.title)
|
|
||||||
Project: $(ldoc.project)
|
|
||||||
Description: $(ldoc.description)
|
|
||||||
# if ldoc.single then
|
|
||||||
(Single module-project)
|
|
||||||
# end
|
|
||||||
# if not module then
|
|
||||||
|
|
||||||
Project contents:
|
|
||||||
# for kind, mods in ldoc.kinds() do
|
|
||||||
$(kind)
|
|
||||||
# for m in mods() do
|
|
||||||
$(m.name): $(m.summary)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
*/
|
|
||||||
# else -- if not module
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Module: $(module.name)
|
|
||||||
Summary: $(module.summary)
|
|
||||||
Description: $(module.description)
|
|
||||||
|
|
||||||
Module contents:
|
|
||||||
# for kind, items in module.kinds() do
|
|
||||||
$(kind)
|
|
||||||
# for item in items() do
|
|
||||||
$(item.type) $(displayName(item))
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Completions */
|
|
||||||
{
|
|
||||||
"scope": "$(e(scope))",
|
|
||||||
|
|
||||||
"completions": [
|
|
||||||
# for kind, items in module.kinds() do
|
|
||||||
/* $(kind) */
|
|
||||||
# for item in items() do
|
|
||||||
/*
|
|
||||||
$(item.type) $(displayName(item))
|
|
||||||
Summary: $(item.summary)
|
|
||||||
Description: $(indent("\t\t\t", item.description))
|
|
||||||
# if item.type == "function" then
|
|
||||||
Parameters:
|
|
||||||
# for p in item.params:iter() do
|
|
||||||
# local default = item:default_of_param(p)
|
|
||||||
# if default == true then default = "(optional)"
|
|
||||||
# elseif default then default = "(defaults to "..default..")" end
|
|
||||||
($(item:type_of_param(p))) $(item:display_name_of(p)):$(item.params.map[p]) $(default)
|
|
||||||
# end
|
|
||||||
# local retgroups = item.retgroups or {}
|
|
||||||
Returns:
|
|
||||||
# for i, group in ldoc.ipairs(retgroups) do
|
|
||||||
# for ret in group:iter() do
|
|
||||||
($(ret.type)) $(indent("\t\t\t", ret.text))
|
|
||||||
# end
|
|
||||||
# if i < #retgroups then
|
|
||||||
---or---
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# if item.usage then
|
|
||||||
Usage:
|
|
||||||
# for i, usage in ldoc.ipairs(item.usage) do
|
|
||||||
$(sep)$(indent("\t\t\t", usage:gsub("^\n", "")))
|
|
||||||
# if i < #item.usage then
|
|
||||||
--------
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# if item.see then
|
|
||||||
See also:
|
|
||||||
# for see in item.see:iter() do
|
|
||||||
$(see.mod.name): $(see.name)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
*/
|
|
||||||
# for pos in (module.name.."."):gmatch("()[^.]+%.") do
|
|
||||||
# local prefix = e(module.name:sub(pos) .. (item.name:match("^[%.%:]") and "" or "."))
|
|
||||||
{
|
|
||||||
"trigger": "$(prefix)$(e(item.name))\t$(e(item.summary))",
|
|
||||||
"contents": "$(prefix)$(e(autocompleteName(item)))"
|
|
||||||
},
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
]
|
|
||||||
}
|
|
||||||
# end -- if not module
|
|
||||||
|
|
||||||
/* Generated by LDoc; sublime-completions template by Reuh. Last updated $(ldoc.updatetime).*/
|
|
||||||
BIN
icon.png
BIN
icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.2 KiB |
9
libs/sf2dlib/.gitignore
vendored
9
libs/sf2dlib/.gitignore
vendored
|
|
@ -1,7 +1,2 @@
|
||||||
*.d
|
libsf2d/build/
|
||||||
*.o
|
libsf2d/lib/
|
||||||
*.project
|
|
||||||
*.cproject
|
|
||||||
libsf2d/.settings/*
|
|
||||||
libsf2d/build/*
|
|
||||||
libsf2d/lib/*
|
|
||||||
1
libs/sf2dlib/libsf2d/.gitignore
vendored
1
libs/sf2dlib/libsf2d/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
/Default/
|
|
||||||
|
|
@ -30,7 +30,6 @@ CFLAGS := -g -Wall -O2\
|
||||||
$(ARCH)
|
$(ARCH)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
|
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
|
||||||
#CFLAGS += -std=c11
|
|
||||||
|
|
||||||
#WILL HAVE TO BE REMOVED SOON
|
#WILL HAVE TO BE REMOVED SOON
|
||||||
CFLAGS += -DLIBCTRU_NO_DEPRECATION
|
CFLAGS += -DLIBCTRU_NO_DEPRECATION
|
||||||
|
|
@ -139,8 +138,6 @@ $(OUTPUT) : $(OFILES)
|
||||||
@echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h
|
@echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h
|
||||||
@echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h
|
@echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h
|
||||||
|
|
||||||
sf2d.c: shader.vsh
|
|
||||||
|
|
||||||
-include $(DEPENDS)
|
-include $(DEPENDS)
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
* @date 22 March 2015
|
* @date 22 March 2015
|
||||||
* @brief sf2dlib header
|
* @brief sf2dlib header
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SF2D_H
|
#ifndef SF2D_H
|
||||||
#define SF2D_H
|
#define SF2D_H
|
||||||
|
|
||||||
|
|
@ -67,14 +68,6 @@ typedef enum {
|
||||||
TEXFMT_ETC1A4 = 13
|
TEXFMT_ETC1A4 = 13
|
||||||
} sf2d_texfmt;
|
} sf2d_texfmt;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Represents a direction for drawing a gradient
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SF2D_TOP_TO_BOTTOM,
|
|
||||||
SF2D_LEFT_TO_RIGHT
|
|
||||||
} sf2d_gradient_dir;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Data allocated on the RAM or VRAM
|
* @brief Data allocated on the RAM or VRAM
|
||||||
|
|
@ -143,11 +136,6 @@ typedef struct {
|
||||||
void *data; /**< Pointer to the data */
|
void *data; /**< Pointer to the data */
|
||||||
} sf2d_texture;
|
} sf2d_texture;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
sf2d_texture texture; // "inherit"/extend standard texture
|
|
||||||
float projection[4*4]; /**< Orthographic projection matrix for this target */
|
|
||||||
} sf2d_rendertarget;
|
|
||||||
|
|
||||||
// Basic functions
|
// Basic functions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -183,12 +171,6 @@ void sf2d_set_3D(int enable);
|
||||||
*/
|
*/
|
||||||
void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side);
|
void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Starts a frame bound to a rendertarget
|
|
||||||
* @param target rendertarget to draw to
|
|
||||||
*/
|
|
||||||
void sf2d_start_frame_target(sf2d_rendertarget *target);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Ends a frame, should be called on pair with sf2d_start_frame
|
* @brief Ends a frame, should be called on pair with sf2d_start_frame
|
||||||
*/
|
*/
|
||||||
|
|
@ -257,10 +239,9 @@ void sf2d_set_clear_color(u32 color);
|
||||||
* @param y0 y coordinate of the first dot
|
* @param y0 y coordinate of the first dot
|
||||||
* @param x1 x coordinate of the second dot
|
* @param x1 x coordinate of the second dot
|
||||||
* @param y1 y coordinate of the sceond dot
|
* @param y1 y coordinate of the sceond dot
|
||||||
* @param width thickness of the line
|
|
||||||
* @param color the color to draw the line
|
* @param color the color to draw the line
|
||||||
*/
|
*/
|
||||||
void sf2d_draw_line(float x0, float y0, float x1, float y1, float width, u32 color);
|
void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draws a rectangle
|
* @brief Draws a rectangle
|
||||||
|
|
@ -272,18 +253,6 @@ void sf2d_set_clear_color(u32 color);
|
||||||
*/
|
*/
|
||||||
void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color);
|
void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Draws a triangle
|
|
||||||
* @param x1 x coordinate of a vertex of the triangle
|
|
||||||
* @param y1 y coordinate of a vertex of the triangle
|
|
||||||
* @param x2 x coordinate of a vertex of the triangle
|
|
||||||
* @param y2 y coordinate of a vertex of the triangle
|
|
||||||
* @param x3 x coordinate of a vertex of the triangle
|
|
||||||
* @param y3 y coordinate of a vertex of the triangle
|
|
||||||
* @param color the color to draw the triangle
|
|
||||||
*/
|
|
||||||
void sf2d_draw_triangle(float x1, float y1, float x2, float y2, float x3, float y3, u32 color);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draws a rotated rectangle
|
* @brief Draws a rotated rectangle
|
||||||
* @param x x coordinate of the top left corner of the rectangle
|
* @param x x coordinate of the top left corner of the rectangle
|
||||||
|
|
@ -295,31 +264,6 @@ void sf2d_draw_triangle(float x1, float y1, float x2, float y2, float x3, float
|
||||||
*/
|
*/
|
||||||
void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad);
|
void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Draws a rectangle
|
|
||||||
* @param x x coordinate of the top left corner of the rectangle
|
|
||||||
* @param y y coordinate of the top left corner of the rectangle
|
|
||||||
* @param w rectangle width
|
|
||||||
* @param h rectangle height
|
|
||||||
* @param color1 the color at the start of the gradient
|
|
||||||
* @param color2 the color at the end of the gradient
|
|
||||||
* @param left_to_right determines which direction the gradient is in
|
|
||||||
*/
|
|
||||||
void sf2d_draw_rectangle_gradient(int x, int y, int w, int h, u32 color1, u32 color2, sf2d_gradient_dir direction);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Draws a rotated rectangle
|
|
||||||
* @param x x coordinate of the top left corner of the rectangle
|
|
||||||
* @param y y coordinate of the top left corner of the rectangle
|
|
||||||
* @param w rectangle width
|
|
||||||
* @param h rectangle height
|
|
||||||
* @param color1 the color at the start of the gradient
|
|
||||||
* @param color2 the color at the end of the gradient
|
|
||||||
* @param left_to_right determines which direction the gradient is in
|
|
||||||
* @param rad rotation (in radians) to draw the rectangle
|
|
||||||
*/
|
|
||||||
void sf2d_draw_rectangle_gradient_rotate(int x, int y, int w, int h, u32 color1, u32 color2, sf2d_gradient_dir direction, float rad);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draws a filled circle
|
* @brief Draws a filled circle
|
||||||
* @param x x coordinate of the center of the circle
|
* @param x x coordinate of the center of the circle
|
||||||
|
|
@ -348,37 +292,12 @@ void sf2d_draw_fill_circle(int x, int y, int radius, u32 color);
|
||||||
*/
|
*/
|
||||||
sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_format, sf2d_place place);
|
sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_format, sf2d_place place);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates an empty rendertarget.
|
|
||||||
* Functions similarly to sf2d_create_texture.
|
|
||||||
* @param width the width of the texture
|
|
||||||
* @param height the height of the texture
|
|
||||||
* @return a pointer to the newly created rendertarget
|
|
||||||
* @note Before drawing the texture, it needs to be tiled
|
|
||||||
* by calling sf2d_texture_tile32.
|
|
||||||
* The default texture params are both min and mag filters
|
|
||||||
* GPU_NEAREST, and both S and T wrappings GPU_CLAMP_TO_BORDER.
|
|
||||||
*/
|
|
||||||
sf2d_rendertarget *sf2d_create_rendertarget(int width, int height);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Frees a texture
|
* @brief Frees a texture
|
||||||
* @param texture pointer to the texture to freeze
|
* @param texture pointer to the texture to freeze
|
||||||
*/
|
*/
|
||||||
void sf2d_free_texture(sf2d_texture *texture);
|
void sf2d_free_texture(sf2d_texture *texture);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Frees a rendertarget
|
|
||||||
* @param target pointer to the rendertarget to free
|
|
||||||
*/
|
|
||||||
void sf2d_free_target(sf2d_rendertarget *target);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clears a rendertarget to the specified color
|
|
||||||
* @param target pointer to the rendertarget to clear
|
|
||||||
*/
|
|
||||||
void sf2d_clear_target(sf2d_rendertarget *target, u32 color);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Fills an already allocated texture from a RGBA8 source
|
* @brief Fills an already allocated texture from a RGBA8 source
|
||||||
* @param dst pointer to the destination texture to fill
|
* @param dst pointer to the destination texture to fill
|
||||||
|
|
@ -500,33 +419,6 @@ void sf2d_draw_texture_rotate(const sf2d_texture *texture, int x, int y, float r
|
||||||
*/
|
*/
|
||||||
void sf2d_draw_texture_rotate_blend(const sf2d_texture *texture, int x, int y, float rad, u32 color);
|
void sf2d_draw_texture_rotate_blend(const sf2d_texture *texture, int x, int y, float rad, u32 color);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Draws a scaled texture with rotation around its hotspot
|
|
||||||
* @param texture the texture to draw
|
|
||||||
* @param x the x coordinate to draw the texture to
|
|
||||||
* @param y the y coordinate to draw the texture to
|
|
||||||
* @param rad rotation (in radians) to draw the texture
|
|
||||||
* @param x_scale the x scale
|
|
||||||
* @param y_scale the y scale
|
|
||||||
* @param center_x the x position of the hotspot
|
|
||||||
* @param center_y the y position of the hotspot
|
|
||||||
*/
|
|
||||||
void sf2d_draw_texture_rotate_scale_hotspot(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Draws a scaled texture with rotation around its hotspot with color
|
|
||||||
* @param texture the texture to draw
|
|
||||||
* @param x the x coordinate to draw the texture to
|
|
||||||
* @param y the y coordinate to draw the texture to
|
|
||||||
* @param rad rotation (in radians) to draw the texture
|
|
||||||
* @param x_scale the x scale
|
|
||||||
* @param y_scale the y scale
|
|
||||||
* @param center_x the x position of the hotspot
|
|
||||||
* @param center_y the y position of the hotspot
|
|
||||||
* @param color the color to blend with the texture
|
|
||||||
*/
|
|
||||||
void sf2d_draw_texture_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y, u32 color);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draws a part of a texture
|
* @brief Draws a part of a texture
|
||||||
* @param texture the texture to draw
|
* @param texture the texture to draw
|
||||||
|
|
@ -633,24 +525,6 @@ void sf2d_draw_texture_part_rotate_scale(const sf2d_texture *texture, int x, int
|
||||||
*/
|
*/
|
||||||
void sf2d_draw_texture_part_rotate_scale_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, u32 color);
|
void sf2d_draw_texture_part_rotate_scale_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, u32 color);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Draws a part of a texture, with rotation, scaling, color and hotspot
|
|
||||||
* @param texture the texture to draw
|
|
||||||
* @param x the x coordinate to draw the texture to
|
|
||||||
* @param y the y coordinate to draw the texture to
|
|
||||||
* @param rad rotation (in radians) to draw the texture
|
|
||||||
* @param tex_x the starting point (x coordinate) where to start drawing
|
|
||||||
* @param tex_y the starting point (y coordinate) where to start drawing
|
|
||||||
* @param tex_w the width to draw from the starting point
|
|
||||||
* @param tex_h the height to draw from the starting point
|
|
||||||
* @param x_scale the x scale
|
|
||||||
* @param y_scale the y scale
|
|
||||||
* @param center_x the x position of the hotspot
|
|
||||||
* @param center_y the y position of the hotspot
|
|
||||||
* @param color the color to blend with the texture
|
|
||||||
*/
|
|
||||||
void sf2d_draw_texture_part_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, float center_x, float center_y, u32 color);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draws a texture blended in a certain depth
|
* @brief Draws a texture blended in a certain depth
|
||||||
* @param texture the texture to draw
|
* @param texture the texture to draw
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
void GPU_SetDummyTexEnv(u8 num);
|
void GPU_SetDummyTexEnv(u8 num);
|
||||||
|
|
||||||
void sf2d_draw_rectangle_internal(const sf2d_vertex_pos_col *vertices);
|
|
||||||
|
|
||||||
// Vector operations
|
// Vector operations
|
||||||
|
|
||||||
void vector_mult_matrix4x4(const float *msrc, const sf2d_vector_3f *vsrc, sf2d_vector_3f *vdst);
|
void vector_mult_matrix4x4(const float *msrc, const sf2d_vector_3f *vsrc, sf2d_vector_3f *vdst);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#include <string.h>
|
|
||||||
#include "sf2d.h"
|
#include "sf2d.h"
|
||||||
#include "sf2d_private.h"
|
#include "sf2d_private.h"
|
||||||
#include "shader_vsh_shbin.h"
|
#include "shader_vsh_shbin.h"
|
||||||
|
|
@ -33,10 +32,6 @@ static u32 projection_desc = -1;
|
||||||
//Matrix
|
//Matrix
|
||||||
static float ortho_matrix_top[4*4];
|
static float ortho_matrix_top[4*4];
|
||||||
static float ortho_matrix_bot[4*4];
|
static float ortho_matrix_bot[4*4];
|
||||||
//Rendertarget things
|
|
||||||
static sf2d_rendertarget * currentRenderTarget = NULL;
|
|
||||||
static void * targetDepthBuffer;
|
|
||||||
static int targetDepthBufferLen = 0;
|
|
||||||
//Apt hook cookie
|
//Apt hook cookie
|
||||||
static aptHookCookie apt_hook_cookie;
|
static aptHookCookie apt_hook_cookie;
|
||||||
//Functions
|
//Functions
|
||||||
|
|
@ -116,7 +111,6 @@ int sf2d_fini()
|
||||||
linearFree(gpu_cmd);
|
linearFree(gpu_cmd);
|
||||||
vramFree(gpu_fb_addr);
|
vramFree(gpu_fb_addr);
|
||||||
vramFree(gpu_depth_fb_addr);
|
vramFree(gpu_depth_fb_addr);
|
||||||
linearFree(targetDepthBuffer);
|
|
||||||
|
|
||||||
sf2d_initialized = 0;
|
sf2d_initialized = 0;
|
||||||
|
|
||||||
|
|
@ -179,53 +173,6 @@ void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side)
|
||||||
GPU_SetDummyTexEnv(5);
|
GPU_SetDummyTexEnv(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sf2d_start_frame_target(sf2d_rendertarget *target)
|
|
||||||
{
|
|
||||||
sf2d_pool_reset();
|
|
||||||
GPUCMD_SetBufferOffset(0);
|
|
||||||
|
|
||||||
// Upload saved uniform
|
|
||||||
matrix_gpu_set_uniform(target->projection, projection_desc);
|
|
||||||
|
|
||||||
int bufferLen = target->texture.width * target->texture.height * 4; // apparently depth buffer is (or can be) 32bit?
|
|
||||||
if (bufferLen > targetDepthBufferLen) { // expand depth buffer
|
|
||||||
if (targetDepthBufferLen > 0) linearFree(targetDepthBuffer);
|
|
||||||
targetDepthBuffer = linearAlloc(bufferLen);
|
|
||||||
memset(targetDepthBuffer, 0, bufferLen);
|
|
||||||
targetDepthBufferLen = bufferLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
GPU_SetViewport((u32 *)osConvertVirtToPhys(targetDepthBuffer),
|
|
||||||
(u32 *)osConvertVirtToPhys(target->texture.data),
|
|
||||||
0, 0, target->texture.height, target->texture.width);
|
|
||||||
|
|
||||||
currentRenderTarget = target;
|
|
||||||
|
|
||||||
GPU_DepthMap(-1.0f, 0.0f);
|
|
||||||
GPU_SetFaceCulling(GPU_CULL_NONE);
|
|
||||||
GPU_SetStencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00);
|
|
||||||
GPU_SetStencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP);
|
|
||||||
GPU_SetBlendingColor(0,0,0,0);
|
|
||||||
GPU_SetDepthTestAndWriteMask(true, GPU_GEQUAL, GPU_WRITE_ALL);
|
|
||||||
GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 0x1, 0);
|
|
||||||
GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, 0);
|
|
||||||
|
|
||||||
GPU_SetAlphaBlending(
|
|
||||||
GPU_BLEND_ADD,
|
|
||||||
GPU_BLEND_ADD,
|
|
||||||
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA,
|
|
||||||
GPU_ONE, GPU_ZERO
|
|
||||||
);
|
|
||||||
|
|
||||||
GPU_SetAlphaTest(false, GPU_ALWAYS, 0x00);
|
|
||||||
|
|
||||||
GPU_SetDummyTexEnv(1);
|
|
||||||
GPU_SetDummyTexEnv(2);
|
|
||||||
GPU_SetDummyTexEnv(3);
|
|
||||||
GPU_SetDummyTexEnv(4);
|
|
||||||
GPU_SetDummyTexEnv(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_end_frame()
|
void sf2d_end_frame()
|
||||||
{
|
{
|
||||||
GPU_FinishDrawing();
|
GPU_FinishDrawing();
|
||||||
|
|
@ -233,7 +180,6 @@ void sf2d_end_frame()
|
||||||
GPUCMD_FlushAndRun();
|
GPUCMD_FlushAndRun();
|
||||||
gspWaitForP3D();
|
gspWaitForP3D();
|
||||||
|
|
||||||
if (!currentRenderTarget) {
|
|
||||||
//Copy the GPU rendered FB to the screen FB
|
//Copy the GPU rendered FB to the screen FB
|
||||||
if (cur_screen == GFX_TOP) {
|
if (cur_screen == GFX_TOP) {
|
||||||
GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 400),
|
GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 400),
|
||||||
|
|
@ -251,12 +197,6 @@ void sf2d_end_frame()
|
||||||
gpu_fb_addr, clear_color, &gpu_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH,
|
gpu_fb_addr, clear_color, &gpu_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH,
|
||||||
gpu_depth_fb_addr, 0, &gpu_depth_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH);
|
gpu_depth_fb_addr, 0, &gpu_depth_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH);
|
||||||
gspWaitForPSC0();
|
gspWaitForPSC0();
|
||||||
} else {
|
|
||||||
//gspWaitForPPF();
|
|
||||||
//gspWaitForPSC0();
|
|
||||||
sf2d_texture_tile32(&(currentRenderTarget->texture));
|
|
||||||
}
|
|
||||||
currentRenderTarget = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sf2d_swapbuffers()
|
void sf2d_swapbuffers()
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,21 @@
|
||||||
#include "sf2d_private.h"
|
#include "sf2d_private.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#ifndef M_PI
|
void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color)
|
||||||
#define M_PI (3.14159265358979323846)
|
{
|
||||||
#endif
|
sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8);
|
||||||
|
if (!vertices) return;
|
||||||
|
|
||||||
|
vertices[0].position = (sf2d_vector_3f){(float)x0+1.0f, (float)y0+1.0f, SF2D_DEFAULT_DEPTH};
|
||||||
|
vertices[1].position = (sf2d_vector_3f){(float)x0-1.0f, (float)y0-1.0f, SF2D_DEFAULT_DEPTH};
|
||||||
|
vertices[2].position = (sf2d_vector_3f){(float)x1+1.0f, (float)y1+1.0f, SF2D_DEFAULT_DEPTH};
|
||||||
|
vertices[3].position = (sf2d_vector_3f){(float)x1-1.0f, (float)y1-1.0f, SF2D_DEFAULT_DEPTH};
|
||||||
|
|
||||||
|
vertices[0].color = color;
|
||||||
|
vertices[1].color = vertices[0].color;
|
||||||
|
vertices[2].color = vertices[0].color;
|
||||||
|
vertices[3].color = vertices[0].color;
|
||||||
|
|
||||||
void sf2d_setup_env_internal(const sf2d_vertex_pos_col* vertices) {
|
|
||||||
GPU_SetTexEnv(
|
GPU_SetTexEnv(
|
||||||
0,
|
0,
|
||||||
GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR),
|
GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR),
|
||||||
|
|
@ -28,59 +38,10 @@ void sf2d_setup_env_internal(const sf2d_vertex_pos_col* vertices) {
|
||||||
(u64[]){0x10}, // attribute permutations for each buffer
|
(u64[]){0x10}, // attribute permutations for each buffer
|
||||||
(u8[]){2} // number of attributes for each buffer
|
(u8[]){2} // number of attributes for each buffer
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_draw_line(float x0, float y0, float x1, float y1, float width, u32 color)
|
|
||||||
{
|
|
||||||
sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8);
|
|
||||||
if (!vertices) return;
|
|
||||||
|
|
||||||
float dx = x1 - x0;
|
|
||||||
float dy = y1 - y0;
|
|
||||||
|
|
||||||
float nx = -dy;
|
|
||||||
float ny = dx;
|
|
||||||
|
|
||||||
float len = sqrt(nx * nx + ny * ny);
|
|
||||||
|
|
||||||
if (len > 0 ){
|
|
||||||
nx /= len;
|
|
||||||
ny /= len;
|
|
||||||
}
|
|
||||||
|
|
||||||
nx *= width*0.5f;
|
|
||||||
ny *= width*0.5f;
|
|
||||||
|
|
||||||
vertices[0].position = (sf2d_vector_3f){x0+nx, y0+ny, SF2D_DEFAULT_DEPTH};
|
|
||||||
vertices[1].position = (sf2d_vector_3f){x0-nx, y0-ny, SF2D_DEFAULT_DEPTH};
|
|
||||||
|
|
||||||
vertices[2].position = (sf2d_vector_3f){x1+nx, y1+ny, SF2D_DEFAULT_DEPTH};
|
|
||||||
vertices[3].position = (sf2d_vector_3f){x1-nx, y1-ny, SF2D_DEFAULT_DEPTH};
|
|
||||||
|
|
||||||
vertices[0].color = color;
|
|
||||||
vertices[1].color = vertices[0].color;
|
|
||||||
vertices[2].color = vertices[0].color;
|
|
||||||
vertices[3].color = vertices[0].color;
|
|
||||||
|
|
||||||
sf2d_setup_env_internal(vertices);
|
|
||||||
|
|
||||||
GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4);
|
GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sf2d_draw_rectangle_internal(const sf2d_vertex_pos_col *vertices)
|
|
||||||
{
|
|
||||||
sf2d_setup_env_internal(vertices);
|
|
||||||
|
|
||||||
GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_draw_triangle_internal(const sf2d_vertex_pos_col *vertices)
|
|
||||||
{
|
|
||||||
sf2d_setup_env_internal(vertices);
|
|
||||||
|
|
||||||
GPU_DrawArray(GPU_TRIANGLES, 0, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color)
|
void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color)
|
||||||
{
|
{
|
||||||
sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8);
|
sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8);
|
||||||
|
|
@ -96,23 +57,29 @@ void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color)
|
||||||
vertices[2].color = vertices[0].color;
|
vertices[2].color = vertices[0].color;
|
||||||
vertices[3].color = vertices[0].color;
|
vertices[3].color = vertices[0].color;
|
||||||
|
|
||||||
sf2d_draw_rectangle_internal(vertices);
|
GPU_SetTexEnv(
|
||||||
}
|
0,
|
||||||
|
GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR),
|
||||||
|
GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR),
|
||||||
|
GPU_TEVOPERANDS(0, 0, 0),
|
||||||
|
GPU_TEVOPERANDS(0, 0, 0),
|
||||||
|
GPU_REPLACE, GPU_REPLACE,
|
||||||
|
0xFFFFFFFF
|
||||||
|
);
|
||||||
|
|
||||||
void sf2d_draw_triangle(float x1, float y1, float x2, float y2, float x3, float y3, u32 color)
|
GPU_SetAttributeBuffers(
|
||||||
{
|
2, // number of attributes
|
||||||
sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(3 * sizeof(sf2d_vertex_pos_col), 8);
|
(u32*)osConvertVirtToPhys(vertices),
|
||||||
if (!vertices) return;
|
GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE),
|
||||||
|
0xFFFC, //0b1100
|
||||||
|
0x10,
|
||||||
|
1, //number of buffers
|
||||||
|
(u32[]){0x0}, // buffer offsets (placeholders)
|
||||||
|
(u64[]){0x10}, // attribute permutations for each buffer
|
||||||
|
(u8[]){2} // number of attributes for each buffer
|
||||||
|
);
|
||||||
|
|
||||||
vertices[0].position = (sf2d_vector_3f){(float)x1, (float)y1, SF2D_DEFAULT_DEPTH};
|
GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4);
|
||||||
vertices[1].position = (sf2d_vector_3f){(float)x2, (float)y2, SF2D_DEFAULT_DEPTH};
|
|
||||||
vertices[2].position = (sf2d_vector_3f){(float)x3, (float)y3, SF2D_DEFAULT_DEPTH};
|
|
||||||
|
|
||||||
vertices[0].color = color;
|
|
||||||
vertices[1].color = vertices[0].color;
|
|
||||||
vertices[2].color = vertices[0].color;
|
|
||||||
|
|
||||||
sf2d_draw_triangle_internal(vertices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad)
|
void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad)
|
||||||
|
|
@ -143,56 +110,29 @@ void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad
|
||||||
vertices[i].position = (sf2d_vector_3f){rot[i].x + x + w2, rot[i].y + y + h2, rot[i].z};
|
vertices[i].position = (sf2d_vector_3f){rot[i].x + x + w2, rot[i].y + y + h2, rot[i].z};
|
||||||
}
|
}
|
||||||
|
|
||||||
sf2d_draw_rectangle_internal(vertices);
|
GPU_SetTexEnv(
|
||||||
}
|
0,
|
||||||
|
GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR),
|
||||||
|
GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR),
|
||||||
|
GPU_TEVOPERANDS(0, 0, 0),
|
||||||
|
GPU_TEVOPERANDS(0, 0, 0),
|
||||||
|
GPU_REPLACE, GPU_REPLACE,
|
||||||
|
0xFFFFFFFF
|
||||||
|
);
|
||||||
|
|
||||||
void sf2d_draw_rectangle_gradient(int x, int y, int w, int h, u32 color1, u32 color2, sf2d_gradient_dir direction)
|
GPU_SetAttributeBuffers(
|
||||||
{
|
2, // number of attributes
|
||||||
sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8);
|
(u32*)osConvertVirtToPhys(vertices),
|
||||||
if (!vertices) return;
|
GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE),
|
||||||
|
0xFFFC, //0b1100
|
||||||
|
0x10,
|
||||||
|
1, //number of buffers
|
||||||
|
(u32[]){0x0}, // buffer offsets (placeholders)
|
||||||
|
(u64[]){0x10}, // attribute permutations for each buffer
|
||||||
|
(u8[]){2} // number of attributes for each buffer
|
||||||
|
);
|
||||||
|
|
||||||
vertices[0].position = (sf2d_vector_3f){(float)x, (float)y, SF2D_DEFAULT_DEPTH};
|
GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4);
|
||||||
vertices[1].position = (sf2d_vector_3f){(float)x+w, (float)y, SF2D_DEFAULT_DEPTH};
|
|
||||||
vertices[2].position = (sf2d_vector_3f){(float)x, (float)y+h, SF2D_DEFAULT_DEPTH};
|
|
||||||
vertices[3].position = (sf2d_vector_3f){(float)x+w, (float)y+h, SF2D_DEFAULT_DEPTH};
|
|
||||||
|
|
||||||
vertices[0].color = color1;
|
|
||||||
vertices[1].color = (direction == SF2D_LEFT_TO_RIGHT) ? color2 : color1;
|
|
||||||
vertices[2].color = (direction == SF2D_LEFT_TO_RIGHT) ? color1 : color2;
|
|
||||||
vertices[3].color = color2;
|
|
||||||
|
|
||||||
sf2d_draw_rectangle_internal(vertices);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_draw_rectangle_gradient_rotate(int x, int y, int w, int h, u32 color1, u32 color2, sf2d_gradient_dir direction, float rad)
|
|
||||||
{
|
|
||||||
sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8);
|
|
||||||
if (!vertices) return;
|
|
||||||
|
|
||||||
int w2 = w/2.0f;
|
|
||||||
int h2 = h/2.0f;
|
|
||||||
|
|
||||||
vertices[0].position = (sf2d_vector_3f){(float)-w2, (float)-h2, SF2D_DEFAULT_DEPTH};
|
|
||||||
vertices[1].position = (sf2d_vector_3f){(float) w2, (float)-h2, SF2D_DEFAULT_DEPTH};
|
|
||||||
vertices[2].position = (sf2d_vector_3f){(float)-w2, (float) h2, SF2D_DEFAULT_DEPTH};
|
|
||||||
vertices[3].position = (sf2d_vector_3f){(float) w2, (float) h2, SF2D_DEFAULT_DEPTH};
|
|
||||||
|
|
||||||
vertices[0].color = color1;
|
|
||||||
vertices[1].color = (direction == SF2D_LEFT_TO_RIGHT) ? color2 : color1;
|
|
||||||
vertices[2].color = (direction == SF2D_LEFT_TO_RIGHT) ? color1 : color2;
|
|
||||||
vertices[3].color = color2;
|
|
||||||
|
|
||||||
float m[4*4];
|
|
||||||
matrix_set_z_rotation(m, rad);
|
|
||||||
sf2d_vector_3f rot[4];
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
vector_mult_matrix4x4(m, &vertices[i].position, &rot[i]);
|
|
||||||
vertices[i].position = (sf2d_vector_3f){rot[i].x + x + w2, rot[i].y + y + h2, rot[i].z};
|
|
||||||
}
|
|
||||||
|
|
||||||
sf2d_draw_rectangle_internal(vertices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sf2d_draw_fill_circle(int x, int y, int radius, u32 color)
|
void sf2d_draw_fill_circle(int x, int y, int radius, u32 color)
|
||||||
|
|
@ -225,7 +165,27 @@ void sf2d_draw_fill_circle(int x, int y, int radius, u32 color)
|
||||||
vertices[num_segments + 1].position = vertices[1].position;
|
vertices[num_segments + 1].position = vertices[1].position;
|
||||||
vertices[num_segments + 1].color = vertices[1].color;
|
vertices[num_segments + 1].color = vertices[1].color;
|
||||||
|
|
||||||
sf2d_setup_env_internal(vertices);
|
GPU_SetTexEnv(
|
||||||
|
0,
|
||||||
|
GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR),
|
||||||
|
GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR),
|
||||||
|
GPU_TEVOPERANDS(0, 0, 0),
|
||||||
|
GPU_TEVOPERANDS(0, 0, 0),
|
||||||
|
GPU_REPLACE, GPU_REPLACE,
|
||||||
|
0xFFFFFFFF
|
||||||
|
);
|
||||||
|
|
||||||
|
GPU_SetAttributeBuffers(
|
||||||
|
2, // number of attributes
|
||||||
|
(u32*)osConvertVirtToPhys(vertices),
|
||||||
|
GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE),
|
||||||
|
0xFFFC, //0b1100
|
||||||
|
0x10,
|
||||||
|
1, //number of buffers
|
||||||
|
(u32[]){0x0}, // buffer offsets (placeholders)
|
||||||
|
(u64[]){0x10}, // attribute permutations for each buffer
|
||||||
|
(u8[]){2} // number of attributes for each buffer
|
||||||
|
);
|
||||||
|
|
||||||
GPU_DrawArray(GPU_TRIANGLE_FAN, 0, num_segments + 2);
|
GPU_DrawArray(GPU_TRIANGLE_FAN, 0, num_segments + 2);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,6 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "sf2d_private.h"
|
#include "sf2d_private.h"
|
||||||
|
|
||||||
#ifndef M_PI
|
|
||||||
#define M_PI (3.14159265358979323846)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//stolen from staplebutt
|
//stolen from staplebutt
|
||||||
void GPU_SetDummyTexEnv(u8 num)
|
void GPU_SetDummyTexEnv(u8 num)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,7 @@
|
||||||
#include "sf2d.h"
|
#include "sf2d.h"
|
||||||
#include "sf2d_private.h"
|
#include "sf2d_private.h"
|
||||||
|
|
||||||
#ifndef M_PI
|
#define TEX_MIN_SIZE 8
|
||||||
#define M_PI (3.14159265358979323846)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define TEX_MIN_SIZE 32
|
|
||||||
|
|
||||||
static unsigned int nibbles_per_pixel(sf2d_texfmt format)
|
static unsigned int nibbles_per_pixel(sf2d_texfmt format)
|
||||||
{
|
{
|
||||||
|
|
@ -97,22 +93,6 @@ sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_forma
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
sf2d_rendertarget *sf2d_create_rendertarget(int width, int height)
|
|
||||||
{
|
|
||||||
sf2d_texture *tx = sf2d_create_texture(width, height, TEXFMT_RGBA8, SF2D_PLACE_RAM);
|
|
||||||
sf2d_rendertarget *rt = malloc(sizeof(*rt));
|
|
||||||
//memcpy(rt, tx, sizeof(*tx));
|
|
||||||
rt->texture = *tx;
|
|
||||||
free(tx);
|
|
||||||
//tx = * rt->texture;
|
|
||||||
//rt->projection
|
|
||||||
|
|
||||||
matrix_init_orthographic(rt->projection, 0.0f, width, height, 0.0f, 0.0f, 1.0f);
|
|
||||||
matrix_rotate_z(rt->projection, M_PI / 2.0f);
|
|
||||||
|
|
||||||
return rt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_free_texture(sf2d_texture *texture)
|
void sf2d_free_texture(sf2d_texture *texture)
|
||||||
{
|
{
|
||||||
if (texture) {
|
if (texture) {
|
||||||
|
|
@ -125,61 +105,21 @@ void sf2d_free_texture(sf2d_texture *texture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sf2d_free_target(sf2d_rendertarget *target)
|
|
||||||
{
|
|
||||||
sf2d_free_texture(&(target->texture));
|
|
||||||
//free(target); // unnecessary since the texture is the start of the target struct
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_clear_target(sf2d_rendertarget *target, u32 color) {
|
|
||||||
if (color == 0) { // if fully transparent, take a shortcut
|
|
||||||
memset(target->texture.data, 0, target->texture.width * target->texture.height * 4);
|
|
||||||
sf2d_texture_tile32(&(target->texture));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
color = ((color>>24)&0x000000FF) | ((color>>8)&0x0000FF00) | ((color<<8)&0x00FF0000) | ((color<<24)&0xFF000000); // reverse byte order
|
|
||||||
|
|
||||||
int itarget = target->texture.width * target->texture.height;
|
|
||||||
for (int i = 0; i < itarget; i++) { memcpy(target->texture.data + i*4, &color, 4); }
|
|
||||||
|
|
||||||
sf2d_texture_tile32(&(target->texture));
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_texture_tile32_hardware(sf2d_texture *texture, const void *data, int w, int h)
|
|
||||||
{
|
|
||||||
if (texture->tiled) return;
|
|
||||||
const u32 flags = (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) |
|
|
||||||
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) |
|
|
||||||
GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO));
|
|
||||||
|
|
||||||
GSPGPU_FlushDataCache(data, (w*h)<<2);
|
|
||||||
GX_DisplayTransfer(
|
|
||||||
(u32*)data,
|
|
||||||
GX_BUFFER_DIM(w, h),
|
|
||||||
(u32*)texture->data,
|
|
||||||
GX_BUFFER_DIM(texture->pow2_w, texture->pow2_h),
|
|
||||||
flags
|
|
||||||
);
|
|
||||||
gspWaitForPPF();
|
|
||||||
GSPGPU_InvalidateDataCache(texture->data, texture->data_size);
|
|
||||||
texture->tiled = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_fill_texture_from_RGBA8(sf2d_texture *dst, const void *rgba8, int source_w, int source_h)
|
void sf2d_fill_texture_from_RGBA8(sf2d_texture *dst, const void *rgba8, int source_w, int source_h)
|
||||||
{
|
{
|
||||||
// TODO: add support for non-RGBA8 textures
|
// TODO: add support for non-RGBA8 textures
|
||||||
|
|
||||||
u8 *tmp = linearAlloc((dst->pow2_w * dst->pow2_h)<<2);
|
u8 *tmp = linearAlloc(dst->pow2_w * dst->pow2_h * 4);
|
||||||
int i, j;
|
int i, j;
|
||||||
for (i = 0; i < source_h; i++) {
|
for (i = 0; i < source_h; i++) {
|
||||||
for (j = 0; j < source_w; j++) {
|
for (j = 0; j < source_w; j++) {
|
||||||
((u32 *)tmp)[i*dst->pow2_w + j] = __builtin_bswap32(((u32 *)rgba8)[i*source_w + j]);
|
((u32 *)tmp)[i*dst->pow2_w + j] = ((u32 *)rgba8)[i*source_w + j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sf2d_texture_tile32_hardware(dst, tmp, dst->pow2_w, dst->pow2_h);
|
memcpy(dst->data, tmp, dst->pow2_w*dst->pow2_h*4);
|
||||||
linearFree(tmp);
|
linearFree(tmp);
|
||||||
|
|
||||||
|
sf2d_texture_tile32(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
sf2d_texture *sf2d_create_texture_mem_RGBA8(const void *src_buffer, int src_w, int src_h, sf2d_texfmt pixel_format, sf2d_place place)
|
sf2d_texture *sf2d_create_texture_mem_RGBA8(const void *src_buffer, int src_w, int src_h, sf2d_texfmt pixel_format, sf2d_place place)
|
||||||
|
|
@ -404,75 +344,6 @@ void sf2d_draw_texture_rotate_blend(const sf2d_texture *texture, int x, int y, f
|
||||||
color);
|
color);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sf2d_draw_texture_rotate_scale_hotspot_generic(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y)
|
|
||||||
{
|
|
||||||
sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8);
|
|
||||||
if (!vertices) return;
|
|
||||||
|
|
||||||
const float w = texture->width;
|
|
||||||
const float h = texture->height;
|
|
||||||
|
|
||||||
vertices[0].position.x = -center_x * scale_x;
|
|
||||||
vertices[0].position.y = -center_y * scale_y;
|
|
||||||
vertices[0].position.z = SF2D_DEFAULT_DEPTH;
|
|
||||||
|
|
||||||
vertices[1].position.x = (w - center_x) * scale_x;
|
|
||||||
vertices[1].position.y = -center_y * scale_y;
|
|
||||||
vertices[1].position.z = SF2D_DEFAULT_DEPTH;
|
|
||||||
|
|
||||||
vertices[2].position.x = -center_x * scale_x;
|
|
||||||
vertices[2].position.y = (h - center_y) * scale_y;
|
|
||||||
vertices[2].position.z = SF2D_DEFAULT_DEPTH;
|
|
||||||
|
|
||||||
vertices[3].position.x = (w - center_x) * scale_x;
|
|
||||||
vertices[3].position.y = h - center_y * scale_y;
|
|
||||||
vertices[3].position.z = SF2D_DEFAULT_DEPTH;
|
|
||||||
|
|
||||||
float u = w/(float)texture->pow2_w;
|
|
||||||
float v = h/(float)texture->pow2_h;
|
|
||||||
|
|
||||||
vertices[0].texcoord = (sf2d_vector_2f){0.0f, 0.0f};
|
|
||||||
vertices[1].texcoord = (sf2d_vector_2f){u, 0.0f};
|
|
||||||
vertices[2].texcoord = (sf2d_vector_2f){0.0f, v};
|
|
||||||
vertices[3].texcoord = (sf2d_vector_2f){u, v};
|
|
||||||
|
|
||||||
const float c = cosf(rad);
|
|
||||||
const float s = sinf(rad);
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 4; ++i) { // Rotate and translate
|
|
||||||
float _x = vertices[i].position.x;
|
|
||||||
float _y = vertices[i].position.y;
|
|
||||||
vertices[i].position.x = _x*c - _y*s + x;
|
|
||||||
vertices[i].position.y = _x*s + _y*c + y;
|
|
||||||
}
|
|
||||||
|
|
||||||
GPU_SetAttributeBuffers(
|
|
||||||
2, // number of attributes
|
|
||||||
(u32*)osConvertVirtToPhys(vertices),
|
|
||||||
GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT),
|
|
||||||
0xFFFC, //0b1100
|
|
||||||
0x10,
|
|
||||||
1, //number of buffers
|
|
||||||
(u32[]){0x0}, // buffer offsets (placeholders)
|
|
||||||
(u64[]){0x10}, // attribute permutations for each buffer
|
|
||||||
(u8[]){2} // number of attributes for each buffer
|
|
||||||
);
|
|
||||||
|
|
||||||
GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_draw_texture_rotate_scale_hotspot(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y)
|
|
||||||
{
|
|
||||||
sf2d_bind_texture(texture, GPU_TEXUNIT0);
|
|
||||||
sf2d_draw_texture_rotate_scale_hotspot_generic(texture, x, y, rad, scale_x, scale_y, center_x, center_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_draw_texture_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y, u32 color)
|
|
||||||
{
|
|
||||||
sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color);
|
|
||||||
sf2d_draw_texture_rotate_scale_hotspot_generic(texture, x, y, rad, scale_x, scale_y, center_x, center_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sf2d_draw_texture_part_generic(const sf2d_texture *texture, int x, int y, int tex_x, int tex_y, int tex_w, int tex_h)
|
static inline void sf2d_draw_texture_part_generic(const sf2d_texture *texture, int x, int y, int tex_x, int tex_y, int tex_w, int tex_h)
|
||||||
{
|
{
|
||||||
sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8);
|
sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8);
|
||||||
|
|
@ -618,18 +489,18 @@ void sf2d_draw_texture_part_scale_blend(const sf2d_texture *texture, float x, fl
|
||||||
sf2d_draw_texture_part_scale_generic(texture, x, y, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale);
|
sf2d_draw_texture_part_scale_generic(texture, x, y, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sf2d_draw_texture_part_rotate_scale_hotspot_generic(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, float center_x, float center_y)
|
static inline void sf2d_draw_texture_part_rotate_scale_generic(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale)
|
||||||
{
|
{
|
||||||
sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8);
|
sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8);
|
||||||
if (!vertices) return;
|
if (!vertices) return;
|
||||||
|
|
||||||
int w = tex_w;
|
int w2 = (tex_w * x_scale)/2.0f;
|
||||||
int h = tex_h;
|
int h2 = (tex_h * y_scale)/2.0f;
|
||||||
|
|
||||||
vertices[0].position = (sf2d_vector_3f){(float)-center_x * x_scale, (float)-center_y * y_scale, SF2D_DEFAULT_DEPTH};
|
vertices[0].position = (sf2d_vector_3f){(float)-w2, (float)-h2, SF2D_DEFAULT_DEPTH};
|
||||||
vertices[1].position = (sf2d_vector_3f){(float) (w - center_x) * x_scale, (float)-center_y * y_scale, SF2D_DEFAULT_DEPTH};
|
vertices[1].position = (sf2d_vector_3f){(float) w2, (float)-h2, SF2D_DEFAULT_DEPTH};
|
||||||
vertices[2].position = (sf2d_vector_3f){(float)-center_x * x_scale, (float) (h - center_y) * y_scale, SF2D_DEFAULT_DEPTH};
|
vertices[2].position = (sf2d_vector_3f){(float)-w2, (float) h2, SF2D_DEFAULT_DEPTH};
|
||||||
vertices[3].position = (sf2d_vector_3f){(float) (w - center_x) * x_scale, (float) h - center_y * y_scale, SF2D_DEFAULT_DEPTH};
|
vertices[3].position = (sf2d_vector_3f){(float) w2, (float) h2, SF2D_DEFAULT_DEPTH};
|
||||||
|
|
||||||
float u0 = tex_x/(float)texture->pow2_w;
|
float u0 = tex_x/(float)texture->pow2_w;
|
||||||
float v0 = tex_y/(float)texture->pow2_h;
|
float v0 = tex_y/(float)texture->pow2_h;
|
||||||
|
|
@ -669,19 +540,13 @@ static inline void sf2d_draw_texture_part_rotate_scale_hotspot_generic(const sf2
|
||||||
void sf2d_draw_texture_part_rotate_scale(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale)
|
void sf2d_draw_texture_part_rotate_scale(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale)
|
||||||
{
|
{
|
||||||
sf2d_bind_texture(texture, GPU_TEXUNIT0);
|
sf2d_bind_texture(texture, GPU_TEXUNIT0);
|
||||||
sf2d_draw_texture_part_rotate_scale_hotspot_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale, tex_w/2.0f, tex_h/2.0f);
|
sf2d_draw_texture_part_rotate_scale_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sf2d_draw_texture_part_rotate_scale_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, u32 color)
|
void sf2d_draw_texture_part_rotate_scale_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, u32 color)
|
||||||
{
|
{
|
||||||
sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color);
|
sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color);
|
||||||
sf2d_draw_texture_part_rotate_scale_hotspot_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale, tex_w/2.0f, tex_h/2.0f);
|
sf2d_draw_texture_part_rotate_scale_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale);
|
||||||
}
|
|
||||||
|
|
||||||
void sf2d_draw_texture_part_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, float center_x, float center_y, u32 color)
|
|
||||||
{
|
|
||||||
sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color);
|
|
||||||
sf2d_draw_texture_part_rotate_scale_hotspot_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale, center_x, center_y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sf2d_draw_texture_depth_generic(const sf2d_texture *texture, int x, int y, signed short z)
|
static inline void sf2d_draw_texture_depth_generic(const sf2d_texture *texture, int x, int y, signed short z)
|
||||||
|
|
|
||||||
|
|
@ -110,19 +110,9 @@ void sftd_draw_wtextf(sftd_font *font, int x, int y, unsigned int color, unsigne
|
||||||
* @param font the font used to calculate the width
|
* @param font the font used to calculate the width
|
||||||
* @param size the font size
|
* @param size the font size
|
||||||
* @param text a pointer to the text that will be used to calculate the length
|
* @param text a pointer to the text that will be used to calculate the length
|
||||||
* @return the width in pixels
|
|
||||||
*/
|
*/
|
||||||
int sftd_get_text_width(sftd_font *font, unsigned int size, const char *text);
|
int sftd_get_text_width(sftd_font *font, unsigned int size, const char *text);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns the width of the given wide text in pixels
|
|
||||||
* @param font the font used to calculate the width
|
|
||||||
* @param size the font size
|
|
||||||
* @param text a pointer to the wide text that will be used to calculate the length
|
|
||||||
* @return the width in pixels
|
|
||||||
*/
|
|
||||||
int sftd_get_wtext_width(sftd_font *font, unsigned int size, const wchar_t *text);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Draws text using a font. The text will wrap after the pixels specified in lineWidth.
|
* @brief Draws text using a font. The text will wrap after the pixels specified in lineWidth.
|
||||||
* @param font the font to use
|
* @param font the font to use
|
||||||
|
|
@ -159,6 +149,9 @@ void sftd_calc_bounding_box(int *boundingWidth, int *boundingHeight, sftd_font *
|
||||||
*/
|
*/
|
||||||
void sftd_draw_textf_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text, ...);
|
void sftd_draw_textf_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text, ...);
|
||||||
|
|
||||||
|
// (ctruLua addition) Based on sftd_draw_wtext, returns the width of the text drawn.
|
||||||
|
int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -227,13 +227,6 @@ void sftd_draw_text(sftd_font *font, int x, int y, unsigned int color, unsigned
|
||||||
FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
|
FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
|
||||||
|
|
||||||
while (*text) {
|
while (*text) {
|
||||||
if(*text == '\n') {
|
|
||||||
pen_x = x;
|
|
||||||
pen_y += size;
|
|
||||||
text++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text);
|
glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text);
|
||||||
|
|
||||||
if (use_kerning && previous && glyph_index) {
|
if (use_kerning && previous && glyph_index) {
|
||||||
|
|
@ -311,13 +304,6 @@ void sftd_draw_wtext(sftd_font *font, int x, int y, unsigned int color, unsigned
|
||||||
FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
|
FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
|
||||||
|
|
||||||
while (*text) {
|
while (*text) {
|
||||||
if(*text == '\n') {
|
|
||||||
pen_x = x;
|
|
||||||
pen_y += size;
|
|
||||||
text++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text);
|
glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text);
|
||||||
|
|
||||||
if (use_kerning && previous && glyph_index) {
|
if (use_kerning && previous && glyph_index) {
|
||||||
|
|
@ -431,66 +417,6 @@ int sftd_get_text_width(sftd_font *font, unsigned int size, const char *text)
|
||||||
return pen_x;
|
return pen_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sftd_get_wtext_width(sftd_font *font, unsigned int size, const wchar_t *text)
|
|
||||||
{
|
|
||||||
FTC_FaceID face_id = (FTC_FaceID)font;
|
|
||||||
FT_Face face;
|
|
||||||
FTC_Manager_LookupFace(font->ftcmanager, face_id, &face);
|
|
||||||
|
|
||||||
FT_Int charmap_index;
|
|
||||||
charmap_index = FT_Get_Charmap_Index(face->charmap);
|
|
||||||
|
|
||||||
FT_Glyph glyph;
|
|
||||||
FT_Bool use_kerning = FT_HAS_KERNING(face);
|
|
||||||
FT_UInt glyph_index, previous = 0;
|
|
||||||
int pen_x = 0;
|
|
||||||
int pen_y = size;
|
|
||||||
|
|
||||||
FTC_ScalerRec scaler;
|
|
||||||
scaler.face_id = face_id;
|
|
||||||
scaler.width = size;
|
|
||||||
scaler.height = size;
|
|
||||||
scaler.pixel = 1;
|
|
||||||
|
|
||||||
FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
|
|
||||||
|
|
||||||
while (*text) {
|
|
||||||
glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text);
|
|
||||||
|
|
||||||
if (use_kerning && previous && glyph_index) {
|
|
||||||
FT_Vector delta;
|
|
||||||
FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
|
|
||||||
pen_x += delta.x >> 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!texture_atlas_exists(font->tex_atlas, glyph_index)) {
|
|
||||||
FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL);
|
|
||||||
|
|
||||||
if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph, size)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bp2d_rectangle rect;
|
|
||||||
int bitmap_left, bitmap_top;
|
|
||||||
int advance_x, advance_y;
|
|
||||||
int glyph_size;
|
|
||||||
|
|
||||||
texture_atlas_get(font->tex_atlas, glyph_index,
|
|
||||||
&rect, &bitmap_left, &bitmap_top,
|
|
||||||
&advance_x, &advance_y, &glyph_size);
|
|
||||||
|
|
||||||
const float draw_scale = size/(float)glyph_size;
|
|
||||||
|
|
||||||
pen_x += (advance_x >> 16) * draw_scale;
|
|
||||||
pen_y += (advance_y >> 16) * draw_scale;
|
|
||||||
|
|
||||||
previous = glyph_index;
|
|
||||||
text++;
|
|
||||||
}
|
|
||||||
return pen_x;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sftd_draw_text_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text)
|
void sftd_draw_text_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text)
|
||||||
{
|
{
|
||||||
FTC_FaceID face_id = (FTC_FaceID)font;
|
FTC_FaceID face_id = (FTC_FaceID)font;
|
||||||
|
|
@ -684,3 +610,63 @@ void sftd_draw_textf_wrap(sftd_font *font, int x, int y, unsigned int color, uns
|
||||||
sftd_draw_text_wrap(font, x, y, color, size, lineWidth, buffer);
|
sftd_draw_text_wrap(font, x, y, color, size, lineWidth, buffer);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// (ctruLua addition) Based on sftd_draw_wtext, returns the width of the text drawn.
|
||||||
|
int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text)
|
||||||
|
{
|
||||||
|
FTC_FaceID face_id = (FTC_FaceID)font;
|
||||||
|
FT_Face face;
|
||||||
|
FTC_Manager_LookupFace(font->ftcmanager, face_id, &face);
|
||||||
|
|
||||||
|
FT_Int charmap_index;
|
||||||
|
charmap_index = FT_Get_Charmap_Index(face->charmap);
|
||||||
|
|
||||||
|
FT_Glyph glyph;
|
||||||
|
FT_Bool use_kerning = FT_HAS_KERNING(face);
|
||||||
|
FT_UInt glyph_index, previous = 0;
|
||||||
|
int pen_x = 0;
|
||||||
|
|
||||||
|
FTC_ScalerRec scaler;
|
||||||
|
scaler.face_id = face_id;
|
||||||
|
scaler.width = size;
|
||||||
|
scaler.height = size;
|
||||||
|
scaler.pixel = 1;
|
||||||
|
|
||||||
|
FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
|
||||||
|
|
||||||
|
while (*text) {
|
||||||
|
glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text);
|
||||||
|
|
||||||
|
if (use_kerning && previous && glyph_index) {
|
||||||
|
FT_Vector delta;
|
||||||
|
FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
|
||||||
|
pen_x += delta.x >> 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!texture_atlas_exists(font->tex_atlas, glyph_index)) {
|
||||||
|
FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL);
|
||||||
|
|
||||||
|
if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph, size)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bp2d_rectangle rect;
|
||||||
|
int bitmap_left, bitmap_top;
|
||||||
|
int advance_x, advance_y;
|
||||||
|
int glyph_size;
|
||||||
|
|
||||||
|
texture_atlas_get(font->tex_atlas, glyph_index,
|
||||||
|
&rect, &bitmap_left, &bitmap_top,
|
||||||
|
&advance_x, &advance_y, &glyph_size);
|
||||||
|
|
||||||
|
const float draw_scale = size/(float)glyph_size;
|
||||||
|
|
||||||
|
pen_x += (advance_x >> 16) * draw_scale;
|
||||||
|
|
||||||
|
previous = glyph_index;
|
||||||
|
text++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pen_x;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ texture_atlas *texture_atlas_create(int width, int height, sf2d_texfmt format, s
|
||||||
rect.h = height;
|
rect.h = height;
|
||||||
|
|
||||||
atlas->tex = sf2d_create_texture(width, height, format, place);
|
atlas->tex = sf2d_create_texture(width, height, format, place);
|
||||||
sf2d_texture_set_params(atlas->tex, GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR));
|
|
||||||
sf2d_texture_tile32(atlas->tex);
|
sf2d_texture_tile32(atlas->tex);
|
||||||
|
|
||||||
atlas->bp_root = bp2d_create(&rect);
|
atlas->bp_root = bp2d_create(&rect);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,40 +0,0 @@
|
||||||
keyWidth, keyHeight = 25, 25
|
|
||||||
|
|
||||||
layout = {
|
|
||||||
["default"] = {
|
|
||||||
{ "&", "é", "\"", "'", "(", "-", "è", "_", "ç", "à", ")", "=", "Bks" },
|
|
||||||
{ "a", "z", "e", "r", "t", "y", "u", "i", "o", "p", "^", "$", "Ent" },
|
|
||||||
{ "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "ù", "*", "Ent" },
|
|
||||||
{ "Shift", "<", "w", "x", "c", "v", "b", "n", ",", ";", ":", "!", "Tab" },
|
|
||||||
{ "SLck", ">", "+", "/", " ", " ", " ", " ", " ", "{", "}", ".", "Sym" }
|
|
||||||
},
|
|
||||||
["Shift"] = {
|
|
||||||
{ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "°", "+", "Bks" },
|
|
||||||
{ "A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P", "¨", "£", "Ent" },
|
|
||||||
{ "Q", "S", "D", "F", "G", "H", "J", "K", "L", "M", "%", "µ", "Ent" },
|
|
||||||
{ "Shift", ">", "W", "X", "C", "V", "B", "N", "?", ".", "/", "§", "Tab" },
|
|
||||||
{ "SLck", "~", "#", "[", " ", " ", " ", " ", " ", "]", "|", "@", "Sym" }
|
|
||||||
},
|
|
||||||
["Sym"] = {
|
|
||||||
{ "²", "~", "#", "{", "[", "|", "`", "\\", "^", "@", "]", "}", "Bks" },
|
|
||||||
{ "a", "z", "€", "r", "t", "y", "u", "i", "o", "p", "", "¤", "Ent" },
|
|
||||||
{ "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "", "", "Ent" },
|
|
||||||
{ "Shift", "", "w", "x", "c", "v", "b", "n", "", "", "", "", "Tab" },
|
|
||||||
{ "SLck", "", "", "", " ", " ", " ", " ", " ", "", "", "", "Sym" }
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
alias = {
|
|
||||||
["Tab"] = "\t",
|
|
||||||
["Ent"] = "\n",
|
|
||||||
["Bks"] = "\b"
|
|
||||||
}
|
|
||||||
|
|
||||||
sticky = {
|
|
||||||
["SLck"] = "Shift"
|
|
||||||
}
|
|
||||||
|
|
||||||
keys = {
|
|
||||||
["l"] = "Shift",
|
|
||||||
["r"] = "Shift"
|
|
||||||
}
|
|
||||||
|
|
@ -4,23 +4,19 @@ local gfx = require("ctr.gfx")
|
||||||
|
|
||||||
-- Open libs
|
-- Open libs
|
||||||
local keyboard = require("keyboard")
|
local keyboard = require("keyboard")
|
||||||
local filepicker = require("filepicker")
|
local openfile = require("openfile")
|
||||||
local color = dofile("color.lua")
|
local color = dofile("color.lua")
|
||||||
local syntax = dofile("syntax.lua")
|
local syntax = dofile("syntax.lua")
|
||||||
|
|
||||||
-- Load data
|
-- Load data
|
||||||
local font = gfx.font.load(ctr.root .. "resources/VeraMono.ttf")
|
local font = gfx.font.load("VeraMono.ttf")
|
||||||
|
|
||||||
-- Open file
|
-- Open file
|
||||||
local path, binding, mode, key = filepicker(nil, {__default = {
|
local path, status = openfile("Choose a file to edit", nil, nil, "any")
|
||||||
a = {filepicker.openFile, "Open"},
|
if not path then return end
|
||||||
y = {filepicker.newFile, "New File"}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if not mode then return end
|
|
||||||
local lineEnding
|
local lineEnding
|
||||||
local lines = {}
|
local lines = {}
|
||||||
if mode == "open" then
|
if status == "exist" then
|
||||||
for line in io.lines(path, "L") do
|
for line in io.lines(path, "L") do
|
||||||
if not lineEnding then lineEnding = line:match("([\n\r]+)$") end
|
if not lineEnding then lineEnding = line:match("([\n\r]+)$") end
|
||||||
table.insert(lines, line:match("^(.-)[\n\r]*$"))
|
table.insert(lines, line:match("^(.-)[\n\r]*$"))
|
||||||
|
|
@ -35,79 +31,26 @@ local coloredLines = syntax(lines, color)
|
||||||
|
|
||||||
-- Variables
|
-- Variables
|
||||||
local lineHeight = 10
|
local lineHeight = 10
|
||||||
local fontSize = 9
|
|
||||||
local cursorX, cursorY = 1, 1
|
local cursorX, cursorY = 1, 1
|
||||||
local scrollX, scrollY = 0, 0
|
local scrollX, scrollY = 0, 0
|
||||||
local fileModified = false
|
|
||||||
|
|
||||||
-- Helper functions
|
-- Helper functions
|
||||||
local function displayedText(text)
|
local function displayedText(text)
|
||||||
return text:gsub("\t", " "), nil
|
return text:gsub("\t", " ")
|
||||||
end
|
|
||||||
local function drawTop(eye)
|
|
||||||
-- Depth multiplicator. Multiply by a positive and add to x to add depth; multiply by a negative and add to x to go out of the screen.
|
|
||||||
local function d(mul) return math.floor(mul * hid.pos3d() * eye) end
|
|
||||||
|
|
||||||
-- Lines
|
|
||||||
local sI = math.floor(scrollY / lineHeight)
|
|
||||||
if sI < 1 then sI = 1 end
|
|
||||||
|
|
||||||
local eI = math.ceil((scrollY + gfx.TOP_HEIGHT) / lineHeight)
|
|
||||||
if eI > #lines then eI = #lines end
|
|
||||||
|
|
||||||
for i = sI, eI, 1 do
|
|
||||||
local x = -scrollX
|
|
||||||
local y = -scrollY+ (i-1)*lineHeight
|
|
||||||
|
|
||||||
for _,colored in ipairs(coloredLines[i]) do
|
|
||||||
local str = displayedText(colored[1])
|
|
||||||
gfx.text(x + d(#(lines[i]:match("^%s+") or "")*3), y, str, fontSize, colored[2])
|
|
||||||
x = x + font:width(str)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Cursor
|
|
||||||
local curline = lines[cursorY]
|
|
||||||
gfx.rectangle(-scrollX+ font:width(displayedText(curline:sub(1, (utf8.offset(curline, cursorX) or 0)-1))) + d(#(curline:match("^%s+") or "")*3),
|
|
||||||
-scrollY+ (cursorY-1)*lineHeight, 1, lineHeight, 0, color.cursor)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set defaults
|
-- Set defaults
|
||||||
gfx.set3D(true)
|
gfx.set3D(false)
|
||||||
gfx.color.setDefault(color.default)
|
gfx.color.setDefault(color.default)
|
||||||
gfx.color.setBackground(color.background)
|
gfx.color.setBackground(color.background)
|
||||||
gfx.font.setDefault(font)
|
gfx.font.setDefault(font)
|
||||||
gfx.font.setSize(fontSize)
|
|
||||||
|
|
||||||
while ctr.run() do
|
while ctr.run() do
|
||||||
hid.read()
|
hid.read()
|
||||||
local keys = hid.keys()
|
local keys = hid.keys()
|
||||||
|
|
||||||
-- Keys input
|
-- Keys input
|
||||||
if keys.down.start then
|
if keys.down.start then return end
|
||||||
local exit = not fileModified
|
|
||||||
if fileModified then
|
|
||||||
while ctr.run() do
|
|
||||||
hid.read()
|
|
||||||
local keys = hid.keys()
|
|
||||||
if keys.down.b then
|
|
||||||
exit = false
|
|
||||||
break
|
|
||||||
elseif keys.down.a then
|
|
||||||
exit = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
gfx.start(gfx.TOP)
|
|
||||||
gfx.text(3, 3, "The file was modified but not saved!")
|
|
||||||
gfx.text(3, 3 + lineHeight, "Are you sure you want to exit without saving?")
|
|
||||||
gfx.text(3, 3 + lineHeight*2, "Press A to exit and discard the modified file")
|
|
||||||
gfx.text(3, 3 + lineHeight*3, "Press B to return to the editor")
|
|
||||||
gfx.stop()
|
|
||||||
gfx.render()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if exit then break end
|
|
||||||
end
|
|
||||||
|
|
||||||
if keys.down.dRight then
|
if keys.down.dRight then
|
||||||
cursorX = cursorX + 1
|
cursorX = cursorX + 1
|
||||||
|
|
@ -161,14 +104,13 @@ while ctr.run() do
|
||||||
end
|
end
|
||||||
file:flush()
|
file:flush()
|
||||||
file:close()
|
file:close()
|
||||||
fileModified = false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Keyboard input
|
-- Keyboard input
|
||||||
local input = keyboard.read()
|
local input = keyboard.read()
|
||||||
if input then
|
if input then
|
||||||
if input == "\b" then
|
if input == "BACK" then
|
||||||
if cursorX > utf8.len(lines[cursorY])+1 then cursorX = utf8.len(lines[cursorY])+1 end
|
if cursorX > utf8.len(lines[cursorY])+1 then cursorX = utf8.len(lines[cursorY])+1 end
|
||||||
if cursorX > 1 then
|
if cursorX > 1 then
|
||||||
lines[cursorY] = lines[cursorY]:sub(1, utf8.offset(lines[cursorY], cursorX-1)-1)..
|
lines[cursorY] = lines[cursorY]:sub(1, utf8.offset(lines[cursorY], cursorX-1)-1)..
|
||||||
|
|
@ -178,52 +120,60 @@ while ctr.run() do
|
||||||
cursorX, cursorY = utf8.len(lines[cursorY-1])+1, cursorY - 1
|
cursorX, cursorY = utf8.len(lines[cursorY-1])+1, cursorY - 1
|
||||||
lines[cursorY] = lines[cursorY]..lines[cursorY+1]
|
lines[cursorY] = lines[cursorY]..lines[cursorY+1]
|
||||||
table.remove(lines, cursorY+1)
|
table.remove(lines, cursorY+1)
|
||||||
table.remove(coloredLines, cursorY+1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
coloredLines[cursorY] = syntax(lines[cursorY], color)
|
|
||||||
|
|
||||||
elseif input == "\n" then
|
elseif input == "\n" then
|
||||||
local newline = lines[cursorY]:sub(utf8.offset(lines[cursorY], cursorX), -1)
|
local newline = lines[cursorY]:sub(utf8.offset(lines[cursorY], cursorX), -1)
|
||||||
local whitespace = lines[cursorY]:match("^%s+")
|
local whitespace = lines[cursorY]:match("^%s+")
|
||||||
if whitespace then newline = whitespace .. newline end
|
if whitespace then newline = whitespace .. newline end
|
||||||
|
|
||||||
lines[cursorY] = lines[cursorY]:sub(1, utf8.offset(lines[cursorY], cursorX)-1)
|
lines[cursorY] = lines[cursorY]:sub(1, utf8.offset(lines[cursorY], cursorX)-1)
|
||||||
coloredLines[cursorY] = syntax(lines[cursorY], color)
|
|
||||||
table.insert(lines, cursorY + 1, newline)
|
table.insert(lines, cursorY + 1, newline)
|
||||||
table.insert(coloredLines, cursorY + 1, syntax(newline, color))
|
|
||||||
|
|
||||||
cursorX, cursorY = whitespace and #whitespace+1 or 1, cursorY + 1
|
cursorX, cursorY = whitespace and #whitespace+1 or 1, cursorY + 1
|
||||||
|
|
||||||
else
|
else
|
||||||
lines[cursorY] = lines[cursorY]:sub(1, utf8.offset(lines[cursorY], cursorX)-1)..input..
|
lines[cursorY] = lines[cursorY]:sub(1, utf8.offset(lines[cursorY], cursorX)-1)..input..
|
||||||
lines[cursorY]:sub(utf8.offset(lines[cursorY], cursorX), -1)
|
lines[cursorY]:sub(utf8.offset(lines[cursorY], cursorX), -1)
|
||||||
coloredLines[cursorY] = syntax(lines[cursorY], color)
|
|
||||||
cursorX = cursorX + 1
|
cursorX = cursorX + 1
|
||||||
end
|
end
|
||||||
fileModified = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Draw
|
-- Draw
|
||||||
local is3D = hid.pos3d() > 0
|
gfx.start(gfx.TOP)
|
||||||
|
|
||||||
gfx.start(gfx.TOP, gfx.LEFT)
|
-- Lines
|
||||||
drawTop(is3D and 0 or -1)
|
local sI = math.floor(scrollY / lineHeight)
|
||||||
gfx.stop()
|
if sI < 1 then sI = 1 end
|
||||||
|
|
||||||
if is3D then
|
local eI = math.ceil((scrollY + gfx.TOP_HEIGHT) / lineHeight)
|
||||||
gfx.start(gfx.TOP, gfx.RIGHT)
|
if eI > #lines then eI = #lines end
|
||||||
drawTop(1)
|
|
||||||
gfx.stop()
|
for i = sI, eI, 1 do
|
||||||
|
local x = -scrollX
|
||||||
|
local y = -scrollY+ (i-1)*lineHeight
|
||||||
|
|
||||||
|
for _,colored in ipairs(coloredLines[i]) do
|
||||||
|
local str = displayedText(colored[1])
|
||||||
|
|
||||||
|
gfx.color.setDefault(colored[2])
|
||||||
|
gfx.text(x, y, str)
|
||||||
|
gfx.color.setDefault(color.default)
|
||||||
|
|
||||||
|
x = x + font:width(str)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cursor
|
||||||
|
local curline = lines[cursorY]
|
||||||
|
gfx.rectangle(-scrollX+ font:width(displayedText(curline:sub(1, (utf8.offset(curline, cursorX) or 0)-1))),
|
||||||
|
-scrollY+ (cursorY-1)*lineHeight, 1, lineHeight, 0, color.cursor)
|
||||||
|
|
||||||
|
gfx.stop()
|
||||||
|
|
||||||
gfx.start(gfx.BOTTOM)
|
gfx.start(gfx.BOTTOM)
|
||||||
|
|
||||||
gfx.text(3, 3, "FPS: "..math.ceil(gfx.getFPS()))
|
gfx.text(3, 3, "FPS: "..math.ceil(gfx.getFPS()))
|
||||||
gfx.text(3, 3 + lineHeight, "Press select to save.")
|
|
||||||
gfx.text(3, 3 + lineHeight*2, "Press start to exit.")
|
|
||||||
|
|
||||||
keyboard.draw(4, 115)
|
keyboard.draw(5, 115)
|
||||||
|
|
||||||
gfx.stop()
|
gfx.stop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ local syntax = {
|
||||||
return function(lines, color)
|
return function(lines, color)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
|
|
||||||
for _, line in ipairs(type(lines) == "table" and lines or {lines}) do
|
for _,line in ipairs(lines) do
|
||||||
local colored = { { line, color.default } }
|
local colored = { { line, color.default } }
|
||||||
|
|
||||||
for _, patterns in ipairs(syntax) do
|
for _, patterns in ipairs(syntax) do
|
||||||
|
|
@ -86,5 +86,5 @@ return function(lines, color)
|
||||||
table.insert(ret, colored)
|
table.insert(ret, colored)
|
||||||
end
|
end
|
||||||
|
|
||||||
return type(lines) == "table" and ret or ret[1]
|
return ret
|
||||||
end
|
end
|
||||||
|
|
@ -47,7 +47,6 @@ while true do
|
||||||
gfx.text(5, 65, "Speed: "..(speed*100).."% - Left balance: "..(leftBalance*100).."%")
|
gfx.text(5, 65, "Speed: "..(speed*100).."% - Left balance: "..(leftBalance*100).."%")
|
||||||
gfx.stop()
|
gfx.stop()
|
||||||
|
|
||||||
audio.update()
|
|
||||||
gfx.render()
|
gfx.render()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ local models = {
|
||||||
[cfgu.MODEL_N3DSXL] = "New 3DS XL"
|
[cfgu.MODEL_N3DSXL] = "New 3DS XL"
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgu.init()
|
|
||||||
while ctr.run() do
|
while ctr.run() do
|
||||||
hid.read()
|
hid.read()
|
||||||
keys = hid.keys()
|
keys = hid.keys()
|
||||||
|
|
@ -46,7 +45,7 @@ while ctr.run() do
|
||||||
gfx.text(2, 2, "CFGU example")
|
gfx.text(2, 2, "CFGU example")
|
||||||
gfx.text(2, 20, "Region: "..regions[cfgu.getRegion()])
|
gfx.text(2, 20, "Region: "..regions[cfgu.getRegion()])
|
||||||
gfx.text(2, 30, "Model: "..models[cfgu.getModel()])
|
gfx.text(2, 30, "Model: "..models[cfgu.getModel()])
|
||||||
gfx.text(2, 40, "Language: "..languages[cfgu.getLanguage()])
|
gfx.text(2, 40, "Language: "..models[cfgu.getLanguage()])
|
||||||
gfx.text(2, 50, "Username: "..cfgu.getUsername())
|
gfx.text(2, 50, "Username: "..cfgu.getUsername())
|
||||||
local m,d = cfgu.getBirthday()
|
local m,d = cfgu.getBirthday()
|
||||||
gfx.text(2, 60, "Birthday: "..d.."/"..m)
|
gfx.text(2, 60, "Birthday: "..d.."/"..m)
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,8 @@ local dMul = 1
|
||||||
|
|
||||||
local angle = 0
|
local angle = 0
|
||||||
|
|
||||||
local texture1 = gfx.texture.load(ctr.root.."icon.png");
|
local texture1 = gfx.texture.load("sdmc:/3ds/ctruLua/icon.png");
|
||||||
if not texture1 then error("Giants ducks came from another planet") end
|
if not texture1 then error("Giants ducks came from another planet") end
|
||||||
local tWidth, tHeight = texture1:getSize()
|
|
||||||
|
|
||||||
gfx.color.setBackground(gfx.color.RGBA8(200, 200, 200))
|
gfx.color.setBackground(gfx.color.RGBA8(200, 200, 200))
|
||||||
gfx.set3D(true)
|
gfx.set3D(true)
|
||||||
|
|
@ -30,7 +29,7 @@ local function drawStuffIn3D(eye)
|
||||||
gfx.color.setDefault(0xFF0000FF)
|
gfx.color.setDefault(0xFF0000FF)
|
||||||
gfx.rectangle(x + d(10*math.sin(ctr.time()/500)), y, 20, 20, angle)
|
gfx.rectangle(x + d(10*math.sin(ctr.time()/500)), y, 20, 20, angle)
|
||||||
|
|
||||||
gfx.line(50 + d(-6), 50, 75 + d(4), 96, 1, gfx.color.RGBA8(52, 10, 65))
|
gfx.line(50 + d(-6), 50, 75 + d(4), 96, gfx.color.RGBA8(52, 10, 65))
|
||||||
|
|
||||||
gfx.circle(125 + d(-8), 125, 16)
|
gfx.circle(125 + d(-8), 125, 16)
|
||||||
end
|
end
|
||||||
|
|
@ -67,7 +66,7 @@ while ctr.run() do
|
||||||
gfx.text(5, 17, "Hello world, from Lua ! éàçù", 20, gfx.color.RGBA8(0, 0, 0))
|
gfx.text(5, 17, "Hello world, from Lua ! éàçù", 20, gfx.color.RGBA8(0, 0, 0))
|
||||||
gfx.text(5, 50, "Time: "..os.date())
|
gfx.text(5, 50, "Time: "..os.date())
|
||||||
|
|
||||||
texture1:draw(280, 80, angle, tWidth/2, tHeight/2);
|
texture1:draw(280, 80, angle);
|
||||||
|
|
||||||
local cx, cy = hid.circle()
|
local cx, cy = hid.circle()
|
||||||
gfx.rectangle(40, 90, 60, 60, 0, 0xDDDDDDFF)
|
gfx.rectangle(40, 90, 60, 60, 0, 0xDDDDDDFF)
|
||||||
|
|
|
||||||
|
|
@ -25,18 +25,19 @@ while ctr.run() do
|
||||||
dls = dls + 1
|
dls = dls + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
gfx.start(gfx.TOP)
|
gfx.startFrame(gfx.TOP)
|
||||||
gfx.text(0, 0, data)
|
gfx.text(0, 0, data)
|
||||||
gfx.text(0, 20, "Downloaded "..dls.." times.")
|
gfx.text(0, 20, "Downloaded "..dls.." times.")
|
||||||
gfx.stop()
|
gfx.endFrame()
|
||||||
|
|
||||||
gfx.start(gfx.BOTTOM)
|
gfx.startFrame(gfx.BOTTOM)
|
||||||
gfx.text(2, 2, "HTTP Contexts example")
|
gfx.text(2, 2, "HTTP Contexts example")
|
||||||
gfx.text(2, 20, "The data is downloaded from '"..addr.."'.")
|
gfx.text(2, 20, "The data is downloaded from '"..addr.."'.")
|
||||||
gfx.text(2, 30, "Press [B] to redownload.")
|
gfx.text(2, 30, "Press [B] to redownload.")
|
||||||
gfx.stop()
|
gfx.endFrame()
|
||||||
|
|
||||||
gfx.render()
|
gfx.render()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
context:close()
|
context:close()
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ while ctr.run() do
|
||||||
local keys = hid.keys()
|
local keys = hid.keys()
|
||||||
if keys.down.start then break end
|
if keys.down.start then break end
|
||||||
|
|
||||||
local infos = qtm.getHeadtrackingInfo()
|
local infos = qtm.getHeadTrackingInfo()
|
||||||
|
|
||||||
gfx.start(gfx.TOP)
|
gfx.start(gfx.TOP)
|
||||||
if infos:checkHeadFullyDetected() then
|
if infos:checkHeadFullyDetected() then
|
||||||
for i=1, 4 do
|
for i=1, 4 do
|
||||||
gfx.point(infos:convertCoordToScreen(i, 400, 240))
|
gfx.point(infos:convertCoordToScreen(1, 400, 240))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
gfx.stop()
|
gfx.stop()
|
||||||
|
|
|
||||||
|
|
@ -1,318 +0,0 @@
|
||||||
local ctr = require('ctr')
|
|
||||||
local keyboard = require('keyboard')
|
|
||||||
|
|
||||||
local gfx = ctr.gfx
|
|
||||||
|
|
||||||
local externalConfig
|
|
||||||
|
|
||||||
local function gfxPrepare()
|
|
||||||
local old = {gfx.get3D(), gfx.color.getDefault(), gfx.color.getBackground(),
|
|
||||||
gfx.font.getDefault(), gfx.font.getSize()}
|
|
||||||
|
|
||||||
local mono = gfx.font.load(ctr.root .. "resources/VeraMono.ttf")
|
|
||||||
|
|
||||||
gfx.set3D(false)
|
|
||||||
gfx.color.setDefault(0xFFFDFDFD)
|
|
||||||
gfx.color.setBackground(0xFF333333)
|
|
||||||
gfx.font.setDefault(mono)
|
|
||||||
gfx.font.setSize(12)
|
|
||||||
|
|
||||||
return old
|
|
||||||
end
|
|
||||||
|
|
||||||
local function gfxRestore(state)
|
|
||||||
gfx.set3D(state[1])
|
|
||||||
gfx.color.setDefault(state[2])
|
|
||||||
gfx.color.setBackground(state[3])
|
|
||||||
gfx.font.setDefault(state[4])
|
|
||||||
gfx.font.setSize(state[5])
|
|
||||||
end
|
|
||||||
|
|
||||||
local function systemBindings(bindings)
|
|
||||||
bindings.__default.up = {
|
|
||||||
function(_, selected, ...)
|
|
||||||
if selected.inList > 1 then
|
|
||||||
selected.inList = selected.inList - 1
|
|
||||||
if selected.inList == selected.offset then
|
|
||||||
selected.offset = selected.offset - 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
bindings.__default.down = {
|
|
||||||
function(externalConfig, selected, ...)
|
|
||||||
if selected.inList < #externalConfig.fileList then
|
|
||||||
selected.inList = selected.inList + 1
|
|
||||||
if selected.inList - selected.offset >= 16 then
|
|
||||||
selected.offset = selected.offset + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
bindings.__default.left = {
|
|
||||||
function(_, selected, ...)
|
|
||||||
selected.inList, selected.offset = 1, 0
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
bindings.__default.right = {
|
|
||||||
function(externalConfig, selected, ...)
|
|
||||||
selected.inList = #externalConfig.fileList
|
|
||||||
if #externalConfig.fileList > 15 then
|
|
||||||
selected.offset = #externalConfig.fileList - 16
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getFileList(workingDirectory)
|
|
||||||
local fileList = ctr.fs.list(workingDirectory)
|
|
||||||
|
|
||||||
if workingDirectory ~= "/" and workingDirectory ~= "sdmc:/" then
|
|
||||||
table.insert(fileList, {name = "..", isDirectory = true})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Stealy stealing code from original openfile.lua
|
|
||||||
table.sort(fileList, function(i, j)
|
|
||||||
if i.isDirectory and not j.isDirectory then
|
|
||||||
return true
|
|
||||||
elseif i.isDirectory == j.isDirectory then
|
|
||||||
return string.lower(i.name) < string.lower(j.name)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
return fileList
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getBinding(selectedFile, bindings)
|
|
||||||
if selectedFile.isDirectory then
|
|
||||||
return bindings.__directory, "__directory"
|
|
||||||
end
|
|
||||||
for pattern, values in pairs(bindings) do
|
|
||||||
if selectedFile.name:match(pattern) then
|
|
||||||
return values, pattern
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return bindings.__default, "__default"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drawBottom(externalConfig, workingDirectoryScroll, selected)
|
|
||||||
local workingDirectory = externalConfig.workingDirectory
|
|
||||||
local bindings = externalConfig.bindings
|
|
||||||
local selectedFile = externalConfig.fileList[selected.inList]
|
|
||||||
gfx.start(gfx.BOTTOM)
|
|
||||||
gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3)
|
|
||||||
gfx.text(1 - workingDirectoryScroll.value, 0, workingDirectory)
|
|
||||||
if gfx.font.getDefault():width(workingDirectory) > gfx.BOTTOM_WIDTH - 2 then
|
|
||||||
workingDirectoryScroll.value = workingDirectoryScroll.value + workingDirectoryScroll.phase
|
|
||||||
if workingDirectoryScroll.value == (gfx.BOTTOM_WIDTH - 2) - gfx.font.getDefault():width(workingDirectory) or
|
|
||||||
workingDirectoryScroll.value == 0 then
|
|
||||||
workingDirectoryScroll.phase = - workingDirectoryScroll.phase
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
gfx.text(1, 15, selectedFile.name, 12)
|
|
||||||
if not selectedFile.isDirectory then
|
|
||||||
gfx.text(1, 45, tostring(selectedFile.size) .. "B", 12, 0xFF727272)
|
|
||||||
end
|
|
||||||
|
|
||||||
local binding, pattern = getBinding(selectedFile, bindings)
|
|
||||||
if selectedFile.isDirectory then
|
|
||||||
gfx.text(1, 30, bindings.__directory.__name, 12, 0xFF727272)
|
|
||||||
else
|
|
||||||
gfx.text(1, 30, binding.__name, 12, 0xFF727272)
|
|
||||||
end
|
|
||||||
|
|
||||||
local bindingNames = {
|
|
||||||
{"start", "Start"}, {"select", "Select"},
|
|
||||||
{"x", "X"}, {"y", "Y"},
|
|
||||||
{"b", "B"}, {"a", "A"},
|
|
||||||
{"r", "R"}, {"l", "L"},
|
|
||||||
{"zr", "ZR"}, {"zl", "ZL"},
|
|
||||||
{"cstickDown", "C Down"}, {"cstickUp", "C Up"},
|
|
||||||
{"cstickRight", "C Right"}, {"cstickLeft", "C Left"}
|
|
||||||
}
|
|
||||||
|
|
||||||
local j = 0
|
|
||||||
|
|
||||||
for i, v in ipairs(bindingNames) do
|
|
||||||
if binding[v[1]] and binding[v[1]][2] then
|
|
||||||
j = j + 1
|
|
||||||
gfx.text(1, gfx.BOTTOM_HEIGHT - 15*j, v[2] .. ": " .. binding[v[1]][2])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
externalConfig.callbacks.drawBottom(externalConfig, selected)
|
|
||||||
gfx.stop()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drawTop(externalConfig, selected)
|
|
||||||
gfx.start(gfx.TOP)
|
|
||||||
gfx.rectangle(0, (selected.inList-selected.offset-1)*15, gfx.TOP_WIDTH, 16, 0, 0xFF0000B9)
|
|
||||||
local over = #externalConfig.fileList - selected.offset >= 16 and 16 or #externalConfig.fileList - selected.offset
|
|
||||||
for i=selected.offset+1, selected.offset+over do
|
|
||||||
local color = externalConfig.fileList[i].isDirectory and 0xFF727272 or 0xFFFDFDFD
|
|
||||||
gfx.text(1, (i-selected.offset-1)*15+1, externalConfig.fileList[i].name or "", 12, color)
|
|
||||||
end
|
|
||||||
externalConfig.callbacks.drawTop(externalConfig, selected)
|
|
||||||
gfx.stop()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function eventHandler(externalConfig, selected)
|
|
||||||
externalConfig.callbacks.eventHandler(externalConfig, selected)
|
|
||||||
ctr.hid.read()
|
|
||||||
local state = ctr.hid.keys()
|
|
||||||
local binding, pattern = getBinding(externalConfig.fileList[selected.inList], externalConfig.bindings)
|
|
||||||
for k, v in pairs(binding) do
|
|
||||||
if k ~= "__name" and state.down[k] then
|
|
||||||
local a, b, c, key = v[1](externalConfig, selected, pattern, k)
|
|
||||||
if key then return a, b, c, key
|
|
||||||
else return end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for k, v in pairs(externalConfig.bindings.__default) do
|
|
||||||
if k ~= "__name" and state.down[k] then
|
|
||||||
local a, b, c, key = v[1](externalConfig, selected, pattern, k)
|
|
||||||
if key then return a, b, c, key
|
|
||||||
else break end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function nothing(externalConfig, selected, bindingName, bindingKey)
|
|
||||||
-- externalConfig = {workingDirectory=string, bindings=table, callbacks=table, additionalArguments=table, fileList=table}
|
|
||||||
-- selected = {file=string, inList=number, offset=number}
|
|
||||||
-- bindings = {__default/__directory/[regex] = {__name, [keyName] = {(handlingFunction), (name)}}}
|
|
||||||
-- callbacks = {drawTop, drawBottom, eventHandler}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function changeDirectory(externalConfig, selected, bindingName, bindingKey)
|
|
||||||
if externalConfig.fileList[selected.inList].isDirectory then
|
|
||||||
if externalConfig.fileList[selected.inList].name == ".." then
|
|
||||||
externalConfig.workingDirectory = externalConfig.workingDirectory:gsub("[^/]+/$", "")
|
|
||||||
else
|
|
||||||
externalConfig.workingDirectory = externalConfig.workingDirectory .. externalConfig.fileList[selected.inList].name .. "/"
|
|
||||||
end
|
|
||||||
externalConfig.fileList = getFileList(externalConfig.workingDirectory)
|
|
||||||
selected.inList, selected.offset = 1, 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function newFile(externalConfig, selected, bindingName, bindingKey)
|
|
||||||
local name = ""
|
|
||||||
while ctr.run() do
|
|
||||||
gfx.start(gfx.BOTTOM)
|
|
||||||
gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3)
|
|
||||||
gfx.text(1, 0, externalConfig.workingDirectory)
|
|
||||||
keyboard.draw(4, 115)
|
|
||||||
gfx.stop()
|
|
||||||
|
|
||||||
gfx.start(gfx.TOP)
|
|
||||||
gfx.rectangle(0, 0, gfx.TOP_WIDTH, 16, 0, 0xFF0000B3)
|
|
||||||
gfx.text(1, 0, "Creating new file")
|
|
||||||
gfx.rectangle(4, gfx.TOP_HEIGHT // 2 - 15, gfx.TOP_WIDTH - 8, 30, 0, 0xFF727272)
|
|
||||||
gfx.text(5, gfx.TOP_HEIGHT // 2 - 6, name, 12)
|
|
||||||
gfx.stop()
|
|
||||||
gfx.render()
|
|
||||||
|
|
||||||
local char = (keyboard.read() or ""):gsub("[\t%/%?%<%>%\\%:%*%|%”%^]", "")
|
|
||||||
ctr.hid.read()
|
|
||||||
local keys = ctr.hid.keys()
|
|
||||||
|
|
||||||
if char ~= "" and char ~= "\b" and char ~= "\n" then
|
|
||||||
name = name .. char
|
|
||||||
elseif char ~= "" and char == "\b" then
|
|
||||||
name = name:sub(1, (utf8.offset(name, -1) or 0)-1)
|
|
||||||
elseif (char ~= "" and char == "\n" or keys.down.a) and name ~= "" then
|
|
||||||
local b, p = getBinding({name=name}, externalConfig.bindings)
|
|
||||||
return externalConfig.workingDirectory .. name, p, "new", b
|
|
||||||
elseif keys.down.b then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function openFile(externalConfig, selected, bindingName, bindingKey)
|
|
||||||
return externalConfig.workingDirectory .. externalConfig.fileList[selected.inList].name,
|
|
||||||
bindingName, "open", bindingKey
|
|
||||||
end
|
|
||||||
|
|
||||||
local function filePicker(workingDirectory, bindings, callbacks, ...)
|
|
||||||
-- Argument sanitization
|
|
||||||
local additionalArguments = { ... }
|
|
||||||
workingDirectory = workingDirectory or ctr.fs.getDirectory()
|
|
||||||
bindings = bindings or {}
|
|
||||||
callbacks = callbacks or {}
|
|
||||||
for _, v in ipairs({"drawTop", "drawBottom", "eventHandler"}) do
|
|
||||||
if not callbacks[v] then
|
|
||||||
callbacks[v] = function(...) end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if workingDirectory:sub(utf8.offset(workingDirectory, -1) or -1) ~= "/" then
|
|
||||||
workingDirectory = workingDirectory .. "/"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Default Bindings
|
|
||||||
bindings.__default = bindings.__default or {}
|
|
||||||
bindings.__default.__name = bindings.__default.__name or "File"
|
|
||||||
bindings.__default.x = bindings.__default.x or {
|
|
||||||
function(externalConfig, ...)
|
|
||||||
return externalConfig.workingDirectory, "__directory", nil, "x"
|
|
||||||
end, "Quit"
|
|
||||||
}
|
|
||||||
|
|
||||||
bindings.__directory = bindings.__directory or {}
|
|
||||||
bindings.__directory.__name = bindings.__directory.__name or "Directory"
|
|
||||||
bindings.__directory.a = bindings.__directory.a or {
|
|
||||||
changeDirectory, "Open"
|
|
||||||
}
|
|
||||||
|
|
||||||
local movementKeys = {
|
|
||||||
"up" , "down" , "left" , "right" ,
|
|
||||||
"cpadUp", "cpadDown", "cpadLeft", "cpadRight",
|
|
||||||
"dUp" , "dDown" , "dLeft" , "dRight"
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v in pairs(bindings) do
|
|
||||||
if k ~= "__default" then
|
|
||||||
setmetatable(bindings[k], {__index = bindings.__default})
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, w in ipairs(movementKeys) do
|
|
||||||
if v[w] then bindings[k][w] = nil end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
systemBindings(bindings)
|
|
||||||
|
|
||||||
-- Other Initialization
|
|
||||||
local selected = {inList = 1, offset = 0}
|
|
||||||
local workingDirectoryScroll = { value = 0, phase = -1 }
|
|
||||||
local gfxState = gfxPrepare()
|
|
||||||
|
|
||||||
-- Main Loop
|
|
||||||
externalConfig = {workingDirectory=workingDirectory, bindings=bindings,
|
|
||||||
callbacks=callbacks, additionalArguments=additionalArguments,
|
|
||||||
fileList=getFileList(workingDirectory)}
|
|
||||||
while ctr.run() do
|
|
||||||
drawBottom(externalConfig, workingDirectoryScroll, selected)
|
|
||||||
drawTop(externalConfig, selected)
|
|
||||||
gfx.render()
|
|
||||||
|
|
||||||
local file, binding, mode, key = eventHandler(externalConfig, selected)
|
|
||||||
|
|
||||||
if key then
|
|
||||||
gfxRestore(gfxState)
|
|
||||||
return file, binding, mode, key
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local returnTable = {filePicker = filePicker, openFile = openFile,
|
|
||||||
newFile = newFile, changeDirectory = changeDirectory}
|
|
||||||
setmetatable(returnTable, {__call = function(self, ...) return self.filePicker(...) end})
|
|
||||||
|
|
||||||
return returnTable
|
|
||||||
|
|
@ -1,11 +1,44 @@
|
||||||
local ctr = require("ctr")
|
|
||||||
local hid = require("ctr.hid")
|
local hid = require("ctr.hid")
|
||||||
local gfx = require("ctr.gfx")
|
local gfx = require("ctr.gfx")
|
||||||
local hex = gfx.color.hex
|
local hex = gfx.color.hex
|
||||||
|
|
||||||
-- Options
|
-- Options
|
||||||
local config = {}
|
local keyWidth, keyHeight = 25, 25
|
||||||
loadfile(ctr.root .. "config/keyboard.cfg", nil, config)()
|
local layout = {
|
||||||
|
["default"] = {
|
||||||
|
{ "&", "é", "\"", "'", "(", "-", "è", "_", "ç", "à", ")", "=", "Back" },
|
||||||
|
{ "a", "z", "e", "r", "t", "y", "u", "i", "o", "p", "^", "$", "Enter" },
|
||||||
|
{ "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "ù", "*", "Enter" },
|
||||||
|
{ "Shift", "<", "w", "x", "c", "v", "b", "n", ",", ";", ":", "!", "Tab" },
|
||||||
|
{ "CpLck", ">", "+", "/", " ", " ", " ", " ", " ", "{", "}", ".", "AltGr" }
|
||||||
|
},
|
||||||
|
["Shift"] = {
|
||||||
|
{ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "°", "+", "Back" },
|
||||||
|
{ "A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P", "¨", "£", "Enter" },
|
||||||
|
{ "Q", "S", "D", "F", "G", "H", "J", "K", "L", "M", "%", "µ", "Enter" },
|
||||||
|
{ "Shift", ">", "W", "X", "C", "V", "B", "N", "?", ".", "/", "§", "Tab" },
|
||||||
|
{ "CpLck", "~", "#", "[", " ", " ", " ", " ", " ", "]", "|", "@", "AltGr" }
|
||||||
|
},
|
||||||
|
["AltGr"] = {
|
||||||
|
{ "²", "~", "#", "{", "[", "|", "`", "\\", "^", "@", "]", "}", "Back" },
|
||||||
|
{ "a", "z", "€", "r", "t", "y", "u", "i", "o", "p", "", "¤", "Enter" },
|
||||||
|
{ "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "", "", "Enter" },
|
||||||
|
{ "Shift", "", "w", "x", "c", "v", "b", "n", "", "", "", "", "Tab" },
|
||||||
|
{ "CpLck", "", "", "", " ", " ", " ", " ", " ", "", "", "", "AltGr" }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
local alias = {
|
||||||
|
["Tab"] = "\t",
|
||||||
|
["Enter"] = "\n",
|
||||||
|
["Back"] = "BACK"
|
||||||
|
}
|
||||||
|
local sticky = {
|
||||||
|
["CpLck"] = "Shift"
|
||||||
|
}
|
||||||
|
local keys = {
|
||||||
|
["l"] = "Shift",
|
||||||
|
["r"] = "Shift"
|
||||||
|
}
|
||||||
|
|
||||||
-- Variables
|
-- Variables
|
||||||
local currentModifier = { "default", "sticky" }
|
local currentModifier = { "default", "sticky" }
|
||||||
|
|
@ -18,7 +51,7 @@ return {
|
||||||
local xTouch, yTouch
|
local xTouch, yTouch
|
||||||
if hidKeys.down.touch then xTouch, yTouch = hid.touch() end
|
if hidKeys.down.touch then xTouch, yTouch = hid.touch() end
|
||||||
|
|
||||||
for key, modifier in pairs(config.keys) do
|
for key, modifier in pairs(keys) do
|
||||||
if hidKeys.down[key] then
|
if hidKeys.down[key] then
|
||||||
currentModifier = { modifier, "key" }
|
currentModifier = { modifier, "key" }
|
||||||
elseif hidKeys.up[key] and currentModifier[2] == "key" and currentModifier[1] == modifier then
|
elseif hidKeys.up[key] and currentModifier[2] == "key" and currentModifier[1] == modifier then
|
||||||
|
|
@ -26,27 +59,27 @@ return {
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for row, rowKeys in pairs(config.layout[currentModifier[1]]) do
|
for row, rowKeys in pairs(layout[currentModifier[1]]) do
|
||||||
for column, key in pairs(rowKeys) do
|
for column, key in pairs(rowKeys) do
|
||||||
local xKey, yKey = x + (column-1)*(config.keyWidth-1), y + (row-1)*(config.keyHeight-1)
|
local xKey, yKey = x + (column-1)*(keyWidth-1), y + (row-1)*(keyHeight-1)
|
||||||
|
|
||||||
gfx.rectangle(xKey, yKey, config.keyWidth, config.keyHeight, 0, hex(0xFFFFFFFF))
|
gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, hex(0xFFFFFFFF))
|
||||||
gfx.rectangle(xKey + 1, yKey + 1, config.keyWidth - 2, config.keyHeight - 2, 0, hex(0x000000FF))
|
gfx.rectangle(xKey + 1, yKey + 1, keyWidth - 2, keyHeight - 2, 0, hex(0x000000FF))
|
||||||
gfx.text(xKey + 2, yKey + 2, key)
|
gfx.text(xKey + 2, yKey + 2, key)
|
||||||
|
|
||||||
if xTouch then
|
if xTouch then
|
||||||
if xTouch > xKey and xTouch < xKey + config.keyWidth then
|
if xTouch > xKey and xTouch < xKey + keyWidth then
|
||||||
if yTouch > yKey and yTouch < yKey + config.keyHeight then
|
if yTouch > yKey and yTouch < yKey + keyHeight then
|
||||||
gfx.rectangle(xKey, yKey, config.keyWidth, config.keyHeight, 0, hex(0xDDFFFFFF))
|
gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, hex(0xDDFFFFFF))
|
||||||
|
|
||||||
local k = config.alias[key] or key
|
local k = alias[key] or key
|
||||||
if config.sticky[k] and config.layout[config.sticky[k]] then
|
if sticky[k] and layout[sticky[k]] then
|
||||||
if currentModifier[1] == config.sticky[k] and currentModifier[2] == "sticky" then
|
if currentModifier[1] == sticky[k] and currentModifier[2] == "sticky" then
|
||||||
currentModifier = { "default", "sticky" }
|
currentModifier = { "default", "sticky" }
|
||||||
else
|
else
|
||||||
currentModifier = { config.sticky[k], "sticky" }
|
currentModifier = { sticky[k], "sticky" }
|
||||||
end
|
end
|
||||||
elseif config.layout[k] then
|
elseif layout[k] then
|
||||||
if currentModifier[1] == k and currentModifier[2] == "normal" then
|
if currentModifier[1] == k and currentModifier[2] == "normal" then
|
||||||
currentModifier = { "default", "sticky" }
|
currentModifier = { "default", "sticky" }
|
||||||
else
|
else
|
||||||
|
|
|
||||||
156
sdcard/3ds/ctruLua/libs/openfile.lua
Normal file
156
sdcard/3ds/ctruLua/libs/openfile.lua
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
-- Sort ctr.fs.list returns (directories first and alphabetical sorting)
|
||||||
|
local function sort(files)
|
||||||
|
table.sort(files, function(i, j)
|
||||||
|
if i.isDirectory and not j.isDirectory then
|
||||||
|
return true
|
||||||
|
elseif i.isDirectory == j.isDirectory then
|
||||||
|
return string.lower(i.name) < string.lower(j.name)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return files
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Open a file explorer to select a file.
|
||||||
|
-- string title: title of the file explorer.
|
||||||
|
-- string curdir: the directory to initially open the file explorer in, or nil for the current directory.
|
||||||
|
-- string exts: the file extensions the user can select, separated by ";". If nil, all extensions are accepted.
|
||||||
|
-- string type: "exist" to select an existing file, "new" to select an non-existing file or "any" to select a existing
|
||||||
|
-- or non-existing file name. If nil, defaults to "exist".
|
||||||
|
-- returns string: the file the user has selected, or nil if the explorer was closed without selecting a file.
|
||||||
|
-- string: "exist" if the file exist or "new" if it doesn't
|
||||||
|
return function(title, curdir, exts, type)
|
||||||
|
-- Open libs
|
||||||
|
local ctr = require("ctr")
|
||||||
|
local gfx = require("ctr.gfx")
|
||||||
|
|
||||||
|
local keyboard = require("keyboard")
|
||||||
|
|
||||||
|
-- Arguments
|
||||||
|
local curdir = curdir or ctr.fs.getDirectory()
|
||||||
|
local type = type or "exist"
|
||||||
|
|
||||||
|
-- Variables
|
||||||
|
local sel = 1
|
||||||
|
local scroll = 0
|
||||||
|
local files = sort(ctr.fs.list(curdir))
|
||||||
|
|
||||||
|
if curdir ~= "/" then table.insert(files, 1, { name = "..", isDirectory = true }) end
|
||||||
|
local newFileName = ""
|
||||||
|
|
||||||
|
local ret = nil
|
||||||
|
|
||||||
|
-- Remember and set defaults
|
||||||
|
local was3D = gfx.get3D()
|
||||||
|
local wasDefault = gfx.color.getDefault()
|
||||||
|
local wasBackground = gfx.color.getBackground()
|
||||||
|
local wasFont = gfx.font.getDefault()
|
||||||
|
gfx.set3D(false)
|
||||||
|
gfx.color.setDefault(0xFFFFFFFF)
|
||||||
|
gfx.color.setBackground(0xFF000000)
|
||||||
|
gfx.font.setDefault()
|
||||||
|
|
||||||
|
while ctr.run() do
|
||||||
|
ctr.hid.read()
|
||||||
|
local keys = ctr.hid.keys()
|
||||||
|
if keys.down.start then break end
|
||||||
|
|
||||||
|
-- Keys input
|
||||||
|
if keys.down.down and sel < #files then
|
||||||
|
sel = sel + 1
|
||||||
|
if sel > scroll + 14 then scroll = scroll + 1 end
|
||||||
|
elseif keys.down.up and sel > 1 then
|
||||||
|
sel = sel - 1
|
||||||
|
if sel <= scroll then scroll = scroll - 1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
if keys.down.a then
|
||||||
|
local f = files[sel]
|
||||||
|
|
||||||
|
if f.isDirectory then
|
||||||
|
if f.name == ".." then curdir = curdir:gsub("[^/]+/$", "")
|
||||||
|
else curdir = curdir..f.name.."/" end
|
||||||
|
|
||||||
|
sel = 1
|
||||||
|
scroll = 0
|
||||||
|
files = sort(ctr.fs.list(curdir))
|
||||||
|
|
||||||
|
if curdir ~= "/" then
|
||||||
|
table.insert(files, 1, { name = "..", isDirectory = true })
|
||||||
|
end
|
||||||
|
elseif type == "exist" or type == "any" then
|
||||||
|
if exts then
|
||||||
|
for ext in (exts..";"):gmatch("[^;]+") do
|
||||||
|
if f.name:match("%..+$") == ext then
|
||||||
|
ret = { curdir..f.name, "exist" }
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
ret = { curdir..f.name, "exist" }
|
||||||
|
end
|
||||||
|
if ret then break end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Keyboard input
|
||||||
|
if type == "new" or type == "any" then
|
||||||
|
local input = keyboard.read()
|
||||||
|
if input then
|
||||||
|
if input == "BACK" then
|
||||||
|
newFileName = newFileName:sub(1, (utf8.offset(newFileName, -1) or 0)-1)
|
||||||
|
elseif input == "\n" then
|
||||||
|
local fileStatus = "new"
|
||||||
|
local f = io.open(curdir..newFileName)
|
||||||
|
if f then fileStatus = "exist" f:close() end
|
||||||
|
ret = { curdir..newFileName, fileStatus }
|
||||||
|
break
|
||||||
|
else
|
||||||
|
newFileName = newFileName..input
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw
|
||||||
|
gfx.start(gfx.TOP)
|
||||||
|
|
||||||
|
gfx.rectangle(0, 10+(sel-scroll)*15, gfx.TOP_WIDTH, 15, 0, gfx.color.RGBA8(0, 0, 200))
|
||||||
|
|
||||||
|
for i = scroll+1, scroll+14, 1 do
|
||||||
|
local f = files[i]
|
||||||
|
if not f then break end
|
||||||
|
local name = f.isDirectory and "["..f.name.."]" or f.name.." ("..f.fileSize.."b)"
|
||||||
|
if not f.isHidden then gfx.text(5, 12+(i-scroll)*15, name) end
|
||||||
|
end
|
||||||
|
|
||||||
|
gfx.rectangle(0, 0, gfx.TOP_WIDTH, 25, 0, gfx.color.RGBA8(200, 200, 200))
|
||||||
|
gfx.text(3, 3, curdir, 13, gfx.color.RGBA8(0, 0, 0))
|
||||||
|
|
||||||
|
gfx.stop()
|
||||||
|
|
||||||
|
gfx.start(gfx.BOTTOM)
|
||||||
|
|
||||||
|
gfx.text(5, 5, title)
|
||||||
|
gfx.text(5, 20, "Accepted file extensions: "..(exts or "all"))
|
||||||
|
|
||||||
|
if type == "new" or type == "any" then
|
||||||
|
gfx.text(5, 90, newFileName)
|
||||||
|
keyboard.draw(5, 115)
|
||||||
|
end
|
||||||
|
|
||||||
|
gfx.stop()
|
||||||
|
|
||||||
|
gfx.render()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reset defaults
|
||||||
|
gfx.set3D(was3D)
|
||||||
|
gfx.color.setDefault(wasDefault)
|
||||||
|
gfx.color.setBackground(wasBackground)
|
||||||
|
gfx.font.setDefault(wasFont)
|
||||||
|
|
||||||
|
if ret then
|
||||||
|
return table.unpack(ret)
|
||||||
|
else
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -26,7 +26,7 @@ local function draw(self, x, y, rad)
|
||||||
local tsx, tsy = self.texture:getSize()
|
local tsx, tsy = self.texture:getSize()
|
||||||
|
|
||||||
local sx, sy = getBox(tsx, tsy, frame, self.frameSizeX, self.frameSizeY)
|
local sx, sy = getBox(tsx, tsy, frame, self.frameSizeX, self.frameSizeY)
|
||||||
self.texture:drawPart(x, y, sx, sy, self.frameSizeX, self.frameSizeY, rad, self.offsetX, self.offsetY)
|
self.texture:drawPart(x, y, sx, sy, self.frameSizeX, self.frameSizeY, rad)
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
end
|
end
|
||||||
|
|
@ -52,28 +52,20 @@ local function resetTimer(self)
|
||||||
self.frameTimer = ctr.time()
|
self.frameTimer = ctr.time()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function setOffset(self, x, y)
|
|
||||||
self.offsetX = x or 0
|
|
||||||
self.offsetY = y or self.offsetX
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Sprite object constructor
|
-- Sprite object constructor
|
||||||
function mod.new(texture, fsx, fsy)
|
function mod.new(texture, fsx, fsy)
|
||||||
return {
|
return {
|
||||||
texture = texture,
|
texture = texture,
|
||||||
frameSizeX = fsx,
|
frameSizeX = fsx,
|
||||||
frameSizeY = fsy,
|
frameSizeY = fsy,
|
||||||
offsetX = 0,
|
|
||||||
offsetY = 0,
|
|
||||||
animations = {},
|
animations = {},
|
||||||
currentAnimation = 0,
|
currentAnimation = 0,
|
||||||
currentFrame = 1,
|
currentFrame = 1,
|
||||||
frameTimer = ctr.time(),
|
frameTimer = 0,
|
||||||
|
|
||||||
draw = draw,
|
draw = draw,
|
||||||
addAnimation = addAnimation,
|
addAnimation = addAnimation,
|
||||||
setAnimation = setAnimation,
|
setAnimation = setAnimation,
|
||||||
setOffset = setOffset,
|
|
||||||
resetTimer = resetTimer,
|
resetTimer = resetTimer,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,33 @@
|
||||||
local ctr = require("ctr")
|
|
||||||
local fs = require("ctr.fs")
|
|
||||||
local gfx = require("ctr.gfx")
|
local gfx = require("ctr.gfx")
|
||||||
|
local fs = require("ctr.fs")
|
||||||
|
|
||||||
-- Set up path
|
-- Set up path
|
||||||
local ldir = ctr.root.."libs/"
|
local ldir = fs.getDirectory().."libs/"
|
||||||
package.path = package.path..";".. ldir.."?.lua;".. ldir.."?/init.lua"
|
package.path = package.path..";".. ldir.."?.lua;".. ldir.."?/init.lua"
|
||||||
local filepicker = require("filepicker")
|
|
||||||
|
|
||||||
-- Erroring
|
repeat
|
||||||
local function displayError(err, trace)
|
|
||||||
gfx.set3D(false)
|
|
||||||
gfx.color.setBackground(0xFF0000B3)
|
|
||||||
gfx.color.setDefault(0xFFFDFDFD)
|
|
||||||
gfx.font.setSize(12)
|
|
||||||
gfx.font.setDefault(gfx.font.load(ctr.root .. "resources/VeraMono.ttf"))
|
|
||||||
gfx.disableConsole()
|
|
||||||
|
|
||||||
while ctr.run() do
|
|
||||||
gfx.start(gfx.BOTTOM)
|
|
||||||
gfx.text(1, 1, "An error has occured.")
|
|
||||||
gfx.wrappedText(1, 30, err, gfx.BOTTOM_WIDTH-2)
|
|
||||||
gfx.text(1, gfx.BOTTOM_HEIGHT-15, "Press Start to continue.")
|
|
||||||
gfx.stop()
|
|
||||||
gfx.start(gfx.TOP)
|
|
||||||
gfx.wrappedText(2, 6, trace, gfx.TOP_WIDTH - 2)
|
|
||||||
gfx.stop()
|
|
||||||
|
|
||||||
gfx.render()
|
|
||||||
ctr.hid.read()
|
|
||||||
if ctr.hid.keys().down.start then break end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Main loop
|
|
||||||
while ctr.run() do
|
|
||||||
gfx.set3D(false)
|
gfx.set3D(false)
|
||||||
|
gfx.color.setDefault(0xFFFFFFFF)
|
||||||
|
gfx.color.setBackground(0xFF000000)
|
||||||
gfx.font.setDefault()
|
gfx.font.setDefault()
|
||||||
gfx.color.setDefault(0xFFFDFDFD)
|
local file = require("openfile")("Choose a Lua file to execute", nil, ".lua", "exist")
|
||||||
gfx.color.setBackground(0xFF333333)
|
if file then
|
||||||
local file, ext, mode, key = filepicker(ctr.root, {
|
|
||||||
["%.lua$"] = {
|
|
||||||
a = {filepicker.openFile, "Run"},
|
|
||||||
__name = "Lua Script"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if mode then
|
|
||||||
fs.setDirectory(file:match("^(.-)[^/]*$"))
|
fs.setDirectory(file:match("^(.-)[^/]*$"))
|
||||||
xpcall(dofile, function(err) displayError(err, debug.traceback()) end, file)
|
local success, err = pcall(dofile, file)
|
||||||
else
|
if not success then
|
||||||
break
|
local hid = require("ctr.hid")
|
||||||
|
gfx.set3D(false)
|
||||||
|
gfx.color.setDefault(0xFFFFFFFF)
|
||||||
|
gfx.color.setBackground(0xFF000000)
|
||||||
|
gfx.font.setDefault()
|
||||||
|
while true do
|
||||||
|
hid.read()
|
||||||
|
if hid.keys().down.start then break end
|
||||||
|
gfx.start(gfx.TOP)
|
||||||
|
gfx.wrappedText(0, 0, err, gfx.TOP_WIDTH)
|
||||||
|
gfx.stop()
|
||||||
|
gfx.render()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
error("Main process has exited.\nPlease reboot.\nPressing Start does not work yet.")
|
until not file
|
||||||
37
sdcard/3ds/ctruLua/tests/httpc.lua
Normal file
37
sdcard/3ds/ctruLua/tests/httpc.lua
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
local ctr = require("ctr")
|
||||||
|
local gfx = require("ctr.gfx")
|
||||||
|
local hid = require("ctr.hid")
|
||||||
|
local httpc = require("ctr.httpc")
|
||||||
|
|
||||||
|
local err = 0
|
||||||
|
|
||||||
|
--assert(httpc.init())
|
||||||
|
|
||||||
|
local context = assert(httpc.context())
|
||||||
|
|
||||||
|
assert(context:open("http://firew0lf.github.io/"))
|
||||||
|
assert(context:beginRequest())
|
||||||
|
|
||||||
|
local data = assert(context:downloadData())
|
||||||
|
|
||||||
|
while ctr.run() do
|
||||||
|
hid.read()
|
||||||
|
keys = hid.keys()
|
||||||
|
if keys.held.start then break end
|
||||||
|
if keys.down.b then
|
||||||
|
assert(context:open("http://firew0lf.github.io/"))
|
||||||
|
assert(context:beginRequest())
|
||||||
|
data = assert(context:downloadData())
|
||||||
|
data = (data.."!")
|
||||||
|
end
|
||||||
|
|
||||||
|
gfx.start(gfx.TOP)
|
||||||
|
gfx.text(0, 0, data)
|
||||||
|
gfx.stop()
|
||||||
|
|
||||||
|
gfx.render()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
context:close()
|
||||||
|
--httpc.shutdown()
|
||||||
135
source/apt.c
135
source/apt.c
|
|
@ -13,10 +13,51 @@ Used to manage the applets and application status.
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
/***
|
||||||
|
Initialize the APT module. Useless.
|
||||||
|
@function init
|
||||||
|
*/
|
||||||
|
static int apt_init(lua_State *L) {
|
||||||
|
Result ret = aptInit();
|
||||||
|
if (ret!=0) {
|
||||||
|
lua_pushboolean(L, false);
|
||||||
|
lua_pushinteger(L, ret);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushboolean(L, true);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
Shutdown the APT module. Useless, don't use it.
|
||||||
|
@function shutdown
|
||||||
|
*/
|
||||||
|
static int apt_shutdown(lua_State *L) {
|
||||||
|
aptExit();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
Open an APT session. Should only work if you don't use the homebrew menu.
|
||||||
|
@function openSession
|
||||||
|
*/
|
||||||
|
static int apt_openSession(lua_State *L) {
|
||||||
|
aptOpenSession();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
Close the current APT session.
|
||||||
|
@function closeSession
|
||||||
|
*/
|
||||||
|
static int apt_closeSession(lua_State *L) {
|
||||||
|
aptCloseSession();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Set the app status.
|
Set the app status.
|
||||||
@function setStatus
|
@function setStatus
|
||||||
@tparam integer status the new app status
|
|
||||||
*/
|
*/
|
||||||
static int apt_setStatus(lua_State *L) {
|
static int apt_setStatus(lua_State *L) {
|
||||||
APT_AppStatus status = luaL_checkinteger(L, 1);
|
APT_AppStatus status = luaL_checkinteger(L, 1);
|
||||||
|
|
@ -29,13 +70,11 @@ static int apt_setStatus(lua_State *L) {
|
||||||
/***
|
/***
|
||||||
Get the app status.
|
Get the app status.
|
||||||
@function getStatus
|
@function getStatus
|
||||||
@treturn integer the app status
|
|
||||||
*/
|
*/
|
||||||
static int apt_getStatus(lua_State *L) {
|
static int apt_getStatus(lua_State *L) {
|
||||||
APT_AppStatus status = aptGetStatus();
|
APT_AppStatus status = aptGetStatus();
|
||||||
|
|
||||||
lua_pushinteger(L, status);
|
lua_pushinteger(L, status);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,7 +84,6 @@ Return to the Home menu.
|
||||||
*/
|
*/
|
||||||
static int apt_returnToMenu(lua_State *L) {
|
static int apt_returnToMenu(lua_State *L) {
|
||||||
aptReturnToMenu();
|
aptReturnToMenu();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +96,6 @@ static int apt_getStatusPower(lua_State *L) {
|
||||||
u32 status = aptGetStatusPower();
|
u32 status = aptGetStatusPower();
|
||||||
|
|
||||||
lua_pushboolean(L, status);
|
lua_pushboolean(L, status);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,7 +118,6 @@ Signal that the application is ready for sleeping.
|
||||||
*/
|
*/
|
||||||
static int apt_signalReadyForSleep(lua_State *L) {
|
static int apt_signalReadyForSleep(lua_State *L) {
|
||||||
aptSignalReadyForSleep();
|
aptSignalReadyForSleep();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,50 +128,14 @@ Return the Home menu AppID.
|
||||||
*/
|
*/
|
||||||
static int apt_getMenuAppID(lua_State *L) {
|
static int apt_getMenuAppID(lua_State *L) {
|
||||||
lua_pushinteger(L, aptGetMenuAppID());
|
lua_pushinteger(L, aptGetMenuAppID());
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Allow or not the system to enter sleep mode.
|
|
||||||
@function setSleepAllowed
|
|
||||||
@tparam boolean allowed `true` to allow, `false` to disallow
|
|
||||||
*/
|
|
||||||
static int apt_setSleepAllowed(lua_State *L) {
|
|
||||||
bool allowed = lua_toboolean(L, 1);
|
|
||||||
|
|
||||||
aptSetSleepAllowed(allowed);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Check if sleep mode is allowed.
|
|
||||||
@function isSleepAllowed
|
|
||||||
@treturn boolean `true` is allowed, false if not.
|
|
||||||
*/
|
|
||||||
static int apt_isSleepAllowed(lua_State *L) {
|
|
||||||
lua_pushboolean(L, aptIsSleepAllowed());
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Checks whether the system is a New 3DS.
|
|
||||||
@function isNew3DS
|
|
||||||
@treturn boolean `true` if it's a New3DS, false otherwise
|
|
||||||
*/
|
|
||||||
static int apt_isNew3DS(lua_State *L) {
|
|
||||||
bool isNew3ds;
|
|
||||||
|
|
||||||
APT_CheckNew3DS(&isNew3ds);
|
|
||||||
|
|
||||||
lua_pushboolean(L, isNew3ds);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct luaL_Reg apt_lib[] = {
|
static const struct luaL_Reg apt_lib[] = {
|
||||||
|
{"init", apt_init },
|
||||||
|
{"shutdown", apt_shutdown },
|
||||||
|
{"openSession", apt_openSession },
|
||||||
|
{"closeSession", apt_closeSession },
|
||||||
{"setStatus", apt_setStatus },
|
{"setStatus", apt_setStatus },
|
||||||
{"getStatus", apt_getStatus },
|
{"getStatus", apt_getStatus },
|
||||||
{"returnToMenu", apt_returnToMenu },
|
{"returnToMenu", apt_returnToMenu },
|
||||||
|
|
@ -143,9 +143,6 @@ static const struct luaL_Reg apt_lib[] = {
|
||||||
{"setStatusPower", apt_setStatusPower },
|
{"setStatusPower", apt_setStatusPower },
|
||||||
{"signalReadyForSleep", apt_signalReadyForSleep},
|
{"signalReadyForSleep", apt_signalReadyForSleep},
|
||||||
{"getMenuAppID", apt_getMenuAppID },
|
{"getMenuAppID", apt_getMenuAppID },
|
||||||
{ "setSleepAllowed", apt_setSleepAllowed },
|
|
||||||
{ "isSleepAllowed", apt_isSleepAllowed },
|
|
||||||
{ "isNew3DS", apt_isNew3DS },
|
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -271,45 +268,37 @@ struct { char *name; int value; } apt_constants[] = {
|
||||||
*/
|
*/
|
||||||
{"APTSIGNAL_HOMEBUTTON", APTSIGNAL_HOMEBUTTON },
|
{"APTSIGNAL_HOMEBUTTON", APTSIGNAL_HOMEBUTTON },
|
||||||
/***
|
/***
|
||||||
@field APTSIGNAL_HOMEBUTTON2
|
@field APTSIGNAL_PREPARESLEEP
|
||||||
*/
|
*/
|
||||||
{"APTSIGNAL_HOMEBUTTON2", APTSIGNAL_HOMEBUTTON2 },
|
{"APTSIGNAL_PREPARESLEEP", APTSIGNAL_PREPARESLEEP},
|
||||||
/***
|
/***
|
||||||
@field APTSIGNAL_SLEEP_QUERY
|
@field APTSIGNAL_ENTERSLEEP
|
||||||
*/
|
*/
|
||||||
{"APTSIGNAL_SLEEP_QUERY", APTSIGNAL_SLEEP_QUERY },
|
{"APTSIGNAL_ENTERSLEEP", APTSIGNAL_ENTERSLEEP },
|
||||||
/***
|
|
||||||
@field APTSIGNAL_SLEEP_CANCEL
|
|
||||||
*/
|
|
||||||
{"APTSIGNAL_SLEEP_CANCEL", APTSIGNAL_SLEEP_CANCEL},
|
|
||||||
/***
|
|
||||||
@field APTSIGNAL_SLEEP_ENTER
|
|
||||||
*/
|
|
||||||
{"APTSIGNAL_SLEEP_ENTER", APTSIGNAL_SLEEP_ENTER },
|
|
||||||
/***
|
/***
|
||||||
@field APTSIGNAL_WAKEUP
|
@field APTSIGNAL_WAKEUP
|
||||||
*/
|
*/
|
||||||
{"APTSIGNAL_SLEEP_WAKEUP", APTSIGNAL_SLEEP_WAKEUP},
|
{"APTSIGNAL_WAKEUP", APTSIGNAL_WAKEUP },
|
||||||
/***
|
/***
|
||||||
@field APTSIGNAL_SHUTDOWN
|
@field APTSIGNAL_ENABLE
|
||||||
*/
|
*/
|
||||||
{"APTSIGNAL_SHUTDOWN", APTSIGNAL_SHUTDOWN },
|
{"APTSIGNAL_ENABLE", APTSIGNAL_ENABLE },
|
||||||
/***
|
/***
|
||||||
@field APTSIGNAL_POWERBUTTON
|
@field APTSIGNAL_POWERBUTTON
|
||||||
*/
|
*/
|
||||||
{"APTSIGNAL_POWERBUTTON", APTSIGNAL_POWERBUTTON },
|
{"APTSIGNAL_POWERBUTTON", APTSIGNAL_POWERBUTTON },
|
||||||
/***
|
/***
|
||||||
@field APTSIGNAL_POWERBUTTON2
|
@field APTSIGNAL_UTILITY
|
||||||
*/
|
*/
|
||||||
{"APTSIGNAL_POWERBUTTON2", APTSIGNAL_POWERBUTTON2},
|
{"APTSIGNAL_UTILITY", APTSIGNAL_UTILITY },
|
||||||
/***
|
/***
|
||||||
@field APTSIGNAL_TRY_SLEEP
|
@field APTSIGNAL_SLEEPSYSTEM
|
||||||
*/
|
*/
|
||||||
{"APTSIGNAL_TRY_SLEEP", APTSIGNAL_TRY_SLEEP },
|
{"APTSIGNAL_SLEEPSYSTEM", APTSIGNAL_SLEEPSYSTEM },
|
||||||
/***
|
/***
|
||||||
@field APTSIGNAL_ORDERTOCLOSE
|
@field APTSIGNAL_ERROR
|
||||||
*/
|
*/
|
||||||
{"APTSIGNAL_ORDERTOCLOSE", APTSIGNAL_ORDERTOCLOSE},
|
{"APTSIGNAL_ERROR", APTSIGNAL_ERROR },
|
||||||
/***
|
/***
|
||||||
@field APTHOOK_ONSUSPEND
|
@field APTHOOK_ONSUSPEND
|
||||||
*/
|
*/
|
||||||
|
|
@ -338,8 +327,6 @@ struct { char *name; int value; } apt_constants[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
int luaopen_apt_lib(lua_State *L) {
|
int luaopen_apt_lib(lua_State *L) {
|
||||||
aptInit();
|
|
||||||
|
|
||||||
luaL_newlib(L, apt_lib);
|
luaL_newlib(L, apt_lib);
|
||||||
|
|
||||||
for (int i = 0; apt_constants[i].name; i++) {
|
for (int i = 0; apt_constants[i].name; i++) {
|
||||||
|
|
@ -353,7 +340,3 @@ int luaopen_apt_lib(lua_State *L) {
|
||||||
void load_apt_lib(lua_State *L) {
|
void load_apt_lib(lua_State *L) {
|
||||||
luaL_requiref(L, "ctr.apt", luaopen_apt_lib, false);
|
luaL_requiref(L, "ctr.apt", luaopen_apt_lib, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unload_apt_lib(lua_State *L) {
|
|
||||||
aptExit();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
345
source/audio.c
345
source/audio.c
|
|
@ -11,7 +11,6 @@ There are 24 audio channels available, numbered from 0 to 23.
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
@ -23,80 +22,30 @@ There are 24 audio channels available, numbered from 0 to 23.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TYPE_UNKNOWN = -1,
|
TYPE_UNKNOWN = -1,
|
||||||
TYPE_OGG = 0,
|
TYPE_OGG = 0,
|
||||||
TYPE_WAV = 1,
|
TYPE_WAV = 1
|
||||||
TYPE_RAW = 2
|
|
||||||
} filetype;
|
} filetype;
|
||||||
|
|
||||||
// Audio object userdata
|
// Audio object userdata
|
||||||
typedef struct {
|
typedef struct {
|
||||||
filetype type; // file type
|
filetype type; // file type
|
||||||
|
|
||||||
// File type specific
|
// OGG Vorbis specific
|
||||||
union {
|
OggVorbis_File vf; // ogg vorbis file
|
||||||
// OGG Vorbis
|
|
||||||
struct {
|
|
||||||
OggVorbis_File vf;
|
|
||||||
int currentSection; // section and position at the end of the initial data
|
|
||||||
long rawPosition;
|
|
||||||
};
|
|
||||||
// WAV
|
|
||||||
struct {
|
|
||||||
FILE* file;
|
|
||||||
long fileSize;
|
|
||||||
long filePosition; // position at the end of the initial data
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Needed for playback
|
// Needed for playback
|
||||||
float rate; // sample rate (per channel) (Hz)
|
float rate; // sample rate (per channel) (Hz)
|
||||||
u32 channels; // channel count
|
u32 channels; // channel count
|
||||||
u32 encoding; // data encoding (NDSP_ENCODING_*)
|
u32 encoding; // data encoding (NDSP_ENCODING_*)
|
||||||
|
|
||||||
// Initial data
|
|
||||||
u32 nsamples; // numbers of samples in the audio (per channel, not the total)
|
u32 nsamples; // numbers of samples in the audio (per channel, not the total)
|
||||||
u32 size; // number of bytes in the audio (total, ie data size)
|
u32 size; // number of bytes in the audio (total, ie data size)
|
||||||
char* data; // raw audio data
|
char* data; // raw audio data
|
||||||
|
|
||||||
// Other useful data
|
|
||||||
u16 bytePerSample; // bytes per sample (warning: undefined for ADPCM (only for raw data))
|
|
||||||
u32 chunkSize; // size per chunk (for streaming)
|
|
||||||
u32 chunkNsamples; // number of samples per chunk
|
|
||||||
|
|
||||||
// Playing parameters (type-independant)
|
// Playing parameters (type-independant)
|
||||||
float mix[12]; // mix parameters
|
float mix[12]; // mix parameters
|
||||||
ndspInterpType interp; // interpolation type
|
ndspInterpType interp; // interpolation type
|
||||||
double speed; // playing speed
|
double speed; // playing speed
|
||||||
} audio_userdata;
|
} audio_userdata;
|
||||||
|
|
||||||
// Audio stream instance struct (when an audio is played; only used when streaming)
|
|
||||||
typedef struct {
|
|
||||||
audio_userdata* audio;
|
|
||||||
|
|
||||||
bool loop; // loop audio?
|
|
||||||
|
|
||||||
// Current position information
|
|
||||||
union {
|
|
||||||
// OGG
|
|
||||||
struct {
|
|
||||||
int currentSection;
|
|
||||||
long rawPosition;
|
|
||||||
};
|
|
||||||
// WAV
|
|
||||||
long filePosition;
|
|
||||||
};
|
|
||||||
|
|
||||||
double prevStartTime; // audio time when last chunk started playing
|
|
||||||
bool eof; // if reached end of file
|
|
||||||
bool done; // if streaming ended and the stream will be skipped on the next update
|
|
||||||
// (the struct should be keept in memory until replaced or audio stopped or it will break audio:time())
|
|
||||||
|
|
||||||
char* nextData; // the next data to play
|
|
||||||
ndspWaveBuf* nextWaveBuf;
|
|
||||||
|
|
||||||
char* prevData; // the data actually playing
|
|
||||||
ndspWaveBuf* prevWaveBuf;
|
|
||||||
} audio_stream;
|
|
||||||
|
|
||||||
// Indicate if NDSP was initialized or not.
|
// Indicate if NDSP was initialized or not.
|
||||||
// NDSP doesn't work on citra yet.
|
// NDSP doesn't work on citra yet.
|
||||||
// Please only throw an error related to this when using a ndsp function, so other parts of the
|
// Please only throw an error related to this when using a ndsp function, so other parts of the
|
||||||
|
|
@ -106,52 +55,12 @@ bool isAudioInitialized = false;
|
||||||
// Array of the last audio_userdata sent to each channel; channels range from 0 to 23
|
// Array of the last audio_userdata sent to each channel; channels range from 0 to 23
|
||||||
audio_userdata* channels[24];
|
audio_userdata* channels[24];
|
||||||
|
|
||||||
// Array of the audio_instance that needs to be updated when calling audio.update (indexed per channel).
|
|
||||||
audio_stream* streaming[24];
|
|
||||||
|
|
||||||
// Stop playing audio on a channel, and stop streaming/free memory.
|
|
||||||
void stopAudio(int channel) {
|
|
||||||
ndspChnWaveBufClear(channel);
|
|
||||||
|
|
||||||
// Stop streaming and free data
|
|
||||||
if (streaming[channel] != NULL) {
|
|
||||||
audio_stream* stream = streaming[channel];
|
|
||||||
if (stream->nextWaveBuf != NULL) {
|
|
||||||
free(stream->nextWaveBuf);
|
|
||||||
stream->nextWaveBuf = NULL;
|
|
||||||
}
|
|
||||||
if (stream->nextData != NULL) {
|
|
||||||
linearFree(stream->nextData);
|
|
||||||
stream->nextData = NULL;
|
|
||||||
}
|
|
||||||
if (stream->prevWaveBuf != NULL) {
|
|
||||||
free(stream->prevWaveBuf);
|
|
||||||
stream->prevWaveBuf = NULL;
|
|
||||||
}
|
|
||||||
if (stream->prevData != NULL) {
|
|
||||||
linearFree(stream->prevData);
|
|
||||||
stream->prevData = NULL;
|
|
||||||
}
|
|
||||||
free(stream);
|
|
||||||
streaming[channel] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Load an audio file.
|
Load an audio file.
|
||||||
OGG Vorbis and PCM WAV file format are currently supported.
|
OGG Vorbis and PCM WAV file format are currently supported.
|
||||||
(Most WAV files use the PCM encoding).
|
(Most WAV files use the PCM encoding).
|
||||||
NOTE: audio streaming doesn't use threading for now, this means that the decoding will be done on the main thread.
|
|
||||||
It should work fine with WAVs, but with OGG files you may suffer slowdowns when a new chunk of data is decoded.
|
|
||||||
To avoid that, you can either reduce the chunkDuration or disable streaming, but be careful if you do so, audio files
|
|
||||||
can fill the memory really quickly.
|
|
||||||
@function load
|
@function load
|
||||||
@tparam string path path to the file or the data if type is raw
|
@tparam string path path to the file
|
||||||
@tparam[opt=0.1] number chunkDuration if set to -1, streaming will be disabled (all data is loaded in memory at once)
|
|
||||||
Other values are the stream chunk duration in seconds (ctrµLua will load
|
|
||||||
the audio per chunk of x seconds). Note that you need to call audio.update() each
|
|
||||||
frame in order for ctµLua to load new data from audio streams. Two chunks of data
|
|
||||||
will be loaded at the same time at most (one playing, the other ready to be played).
|
|
||||||
@tparam[opt=detect] string type file type, `"ogg"` or `"wav"`.
|
@tparam[opt=detect] string type file type, `"ogg"` or `"wav"`.
|
||||||
If set to `"detect"`, will try to deduce the type from the filename.
|
If set to `"detect"`, will try to deduce the type from the filename.
|
||||||
@treturn[1] audio the loaded audio object
|
@treturn[1] audio the loaded audio object
|
||||||
|
|
@ -160,8 +69,7 @@ can fill the memory really quickly.
|
||||||
*/
|
*/
|
||||||
static int audio_load(lua_State *L) {
|
static int audio_load(lua_State *L) {
|
||||||
const char *path = luaL_checkstring(L, 1);
|
const char *path = luaL_checkstring(L, 1);
|
||||||
double streamChunk = luaL_optnumber(L, 2, 0.1);
|
const char* argType = luaL_optstring(L, 2, "detect");
|
||||||
const char* argType = luaL_optstring(L, 3, "detect");
|
|
||||||
|
|
||||||
// Create userdata
|
// Create userdata
|
||||||
audio_userdata *audio = lua_newuserdata(L, sizeof(*audio));
|
audio_userdata *audio = lua_newuserdata(L, sizeof(*audio));
|
||||||
|
|
@ -183,8 +91,6 @@ static int audio_load(lua_State *L) {
|
||||||
type = TYPE_OGG;
|
type = TYPE_OGG;
|
||||||
} else if (strcmp(argType, "wav") == 0) {
|
} else if (strcmp(argType, "wav") == 0) {
|
||||||
type = TYPE_WAV;
|
type = TYPE_WAV;
|
||||||
} else if (strcmp(argType, "raw") == 0) {
|
|
||||||
type = TYPE_RAW;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open and read file
|
// Open and read file
|
||||||
|
|
@ -208,26 +114,16 @@ static int audio_load(lua_State *L) {
|
||||||
audio->encoding = NDSP_ENCODING_PCM16;
|
audio->encoding = NDSP_ENCODING_PCM16;
|
||||||
audio->nsamples = ov_pcm_total(&audio->vf, -1);
|
audio->nsamples = ov_pcm_total(&audio->vf, -1);
|
||||||
audio->size = audio->nsamples * audio->channels * 2; // *2 because output is PCM16 (2 bytes/sample)
|
audio->size = audio->nsamples * audio->channels * 2; // *2 because output is PCM16 (2 bytes/sample)
|
||||||
audio->bytePerSample = 2;
|
|
||||||
|
|
||||||
// Streaming
|
if (linearSpaceFree() < audio->size) luaL_error(L, "not enough linear memory available");
|
||||||
if (streamChunk < 0) {
|
audio->data = linearAlloc(audio->size);
|
||||||
audio->chunkNsamples = audio->nsamples;
|
|
||||||
audio->chunkSize = audio->size;
|
|
||||||
} else {
|
|
||||||
audio->chunkNsamples = fmin(round(streamChunk * audio->rate), audio->nsamples);
|
|
||||||
audio->chunkSize = audio->chunkNsamples * audio->channels * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate
|
|
||||||
if (linearSpaceFree() < audio->chunkSize) luaL_error(L, "not enough linear memory available");
|
|
||||||
audio->data = linearAlloc(audio->chunkSize);
|
|
||||||
|
|
||||||
// Decoding loop
|
// Decoding loop
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int eof = 0;
|
int eof = 0;
|
||||||
while (!eof && offset < audio->chunkSize) {
|
int current_section;
|
||||||
long ret = ov_read(&audio->vf, &audio->data[offset], fmin(audio->chunkSize - offset, 4096), &audio->currentSection);
|
while (!eof) {
|
||||||
|
long ret = ov_read(&audio->vf, &audio->data[offset], 4096, ¤t_section);
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
eof = 1;
|
eof = 1;
|
||||||
|
|
@ -241,7 +137,6 @@ static int audio_load(lua_State *L) {
|
||||||
offset += ret;
|
offset += ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
audio->rawPosition = ov_raw_tell(&audio->vf);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
|
@ -327,29 +222,13 @@ static int audio_load(lua_State *L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio->bytePerSample = byte_per_sample / audio->channels;
|
|
||||||
|
|
||||||
// Streaming
|
|
||||||
if (streamChunk < 0) {
|
|
||||||
audio->chunkNsamples = audio->nsamples;
|
|
||||||
audio->chunkSize = audio->size;
|
|
||||||
} else {
|
|
||||||
audio->chunkNsamples = fmin(round(streamChunk * audio->rate), audio->nsamples);
|
|
||||||
audio->chunkSize = audio->chunkNsamples * audio->channels * audio->bytePerSample;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read data
|
// Read data
|
||||||
if (linearSpaceFree() < audio->chunkSize) luaL_error(L, "not enough linear memory available");
|
if (linearSpaceFree() < audio->size) luaL_error(L, "not enough linear memory available");
|
||||||
audio->data = linearAlloc(audio->chunkSize);
|
audio->data = linearAlloc(audio->size);
|
||||||
|
|
||||||
fread(audio->data, audio->chunkSize, 1, file);
|
fread(audio->data, audio->size, 1, file);
|
||||||
|
|
||||||
audio->file = file;
|
|
||||||
audio->filePosition = ftell(file);
|
|
||||||
|
|
||||||
fseek(file, 0, SEEK_END);
|
|
||||||
audio->fileSize = ftell(file);
|
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -365,7 +244,6 @@ static int audio_load(lua_State *L) {
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Load raw audio data from a string.
|
Load raw audio data from a string.
|
||||||
No streaming.
|
|
||||||
@function loadRaw
|
@function loadRaw
|
||||||
@tparam string data raw audio data
|
@tparam string data raw audio data
|
||||||
@tparam number rate sampling rate
|
@tparam number rate sampling rate
|
||||||
|
|
@ -386,18 +264,16 @@ static int audio_loadRaw(lua_State *L) {
|
||||||
luaL_getmetatable(L, "LAudio");
|
luaL_getmetatable(L, "LAudio");
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
audio->type = TYPE_RAW;
|
audio->type = TYPE_WAV;
|
||||||
audio->rate = rate;
|
audio->rate = rate;
|
||||||
audio->channels = channels;
|
audio->channels = channels;
|
||||||
|
|
||||||
u8 sampleSize = 2; // default to 2
|
u8 sampleSize = 2; // default to 2
|
||||||
if (strcmp(argEncoding, "PCM8")) {
|
if (strcmp(argEncoding, "PCM8")) {
|
||||||
audio->encoding = NDSP_ENCODING_PCM8;
|
audio->encoding = NDSP_ENCODING_PCM8;
|
||||||
audio->bytePerSample = 1;
|
|
||||||
sampleSize = 1;
|
sampleSize = 1;
|
||||||
} else if (strcmp(argEncoding, "PCM16")) {
|
} else if (strcmp(argEncoding, "PCM16")) {
|
||||||
audio->encoding = NDSP_ENCODING_PCM16;
|
audio->encoding = NDSP_ENCODING_PCM16;
|
||||||
audio->bytePerSample = 2;
|
|
||||||
} else if (strcmp(argEncoding, "ADPCM")) {
|
} else if (strcmp(argEncoding, "ADPCM")) {
|
||||||
audio->encoding = NDSP_ENCODING_ADPCM;
|
audio->encoding = NDSP_ENCODING_ADPCM;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -410,9 +286,6 @@ static int audio_loadRaw(lua_State *L) {
|
||||||
audio->size = dataSize;
|
audio->size = dataSize;
|
||||||
audio->data = data;
|
audio->data = data;
|
||||||
|
|
||||||
audio->chunkSize = audio->size;
|
|
||||||
audio->chunkNsamples = audio->nsamples;
|
|
||||||
|
|
||||||
audio->speed = 1.0;
|
audio->speed = 1.0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -572,7 +445,7 @@ static int audio_stop(lua_State *L) {
|
||||||
if (channel == -1) {
|
if (channel == -1) {
|
||||||
for (int i = 0; i <= 23; i++) {
|
for (int i = 0; i <= 23; i++) {
|
||||||
if (ndspChnIsPlaying(i)) {
|
if (ndspChnIsPlaying(i)) {
|
||||||
stopAudio(i);
|
ndspChnWaveBufClear(i);
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -580,7 +453,7 @@ static int audio_stop(lua_State *L) {
|
||||||
luaL_error(L, "channel number must be between 0 and 23");
|
luaL_error(L, "channel number must be between 0 and 23");
|
||||||
} else {
|
} else {
|
||||||
if (ndspChnIsPlaying(channel)) {
|
if (ndspChnIsPlaying(channel)) {
|
||||||
stopAudio(channel);
|
ndspChnWaveBufClear(channel);
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -590,128 +463,6 @@ static int audio_stop(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Update all the currently playing audio streams.
|
|
||||||
Must be called every frame if you want to use audio with streaming.
|
|
||||||
@function update
|
|
||||||
*/
|
|
||||||
static int audio_update(lua_State *L) {
|
|
||||||
if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly");
|
|
||||||
|
|
||||||
for (int i = 0; i <= 23; i++) {
|
|
||||||
if (streaming[i] == NULL) continue;
|
|
||||||
audio_stream* stream = streaming[i];
|
|
||||||
if (stream->done) continue;
|
|
||||||
audio_userdata* audio = stream->audio;
|
|
||||||
|
|
||||||
// If the next chunk started to play, load the next one
|
|
||||||
if (stream->nextWaveBuf != NULL && ndspChnGetWaveBufSeq(i) == stream->nextWaveBuf->sequence_id) {
|
|
||||||
if (stream->prevWaveBuf) stream->prevStartTime = stream->prevStartTime + (double)(audio->chunkNsamples) / audio->rate;
|
|
||||||
|
|
||||||
if (!stream->eof) {
|
|
||||||
// Swap buffers
|
|
||||||
char* prevData = stream->prevData; // doesn't contain important data, can rewrite
|
|
||||||
char* nextData = stream->nextData; // contains the data that started playing
|
|
||||||
stream->prevData = nextData; // buffer in use
|
|
||||||
stream->nextData = prevData; // now contains an available buffer
|
|
||||||
stream->prevWaveBuf = stream->nextWaveBuf;
|
|
||||||
|
|
||||||
// Decoding loop
|
|
||||||
u32 chunkNsamples = audio->chunkNsamples; // chunk nsamples and size may be lower than the defaults if reached EOF
|
|
||||||
u32 chunkSize = audio->chunkSize;
|
|
||||||
if (audio->type == TYPE_OGG) {
|
|
||||||
if (ov_seekable(&audio->vf) && ov_raw_tell(&audio->vf) != stream->rawPosition)
|
|
||||||
ov_raw_seek(&audio->vf, stream->rawPosition); // goto last read end (audio file may be played multiple times at one)
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
while (!stream->eof && offset < audio->chunkSize) {
|
|
||||||
long ret = ov_read(&audio->vf, &stream->nextData[offset], fmin(audio->chunkSize - offset, 4096), &stream->currentSection);
|
|
||||||
if (ret == 0) {
|
|
||||||
stream->eof = 1;
|
|
||||||
} else if (ret < 0) {
|
|
||||||
luaL_error(L, "error in the ogg vorbis stream");
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
offset += ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream->rawPosition = ov_raw_tell(&audio->vf);
|
|
||||||
chunkSize = offset;
|
|
||||||
chunkNsamples = chunkSize / audio->channels / audio->bytePerSample;
|
|
||||||
|
|
||||||
} else if (audio->type == TYPE_WAV) {
|
|
||||||
chunkSize = fmin(audio->fileSize - stream->filePosition, audio->chunkSize);
|
|
||||||
chunkNsamples = chunkSize / audio->channels / audio->bytePerSample;
|
|
||||||
|
|
||||||
fseek(audio->file, stream->filePosition, SEEK_SET); // goto last read end (audio file may be played multiple times at one)
|
|
||||||
fread(stream->nextData, chunkSize, 1, audio->file);
|
|
||||||
stream->filePosition = ftell(audio->file);
|
|
||||||
if (stream->filePosition == audio->fileSize) stream->eof = 1;
|
|
||||||
|
|
||||||
} else luaL_error(L, "unknown audio type");
|
|
||||||
|
|
||||||
// Send & play audio data
|
|
||||||
ndspWaveBuf* waveBuf = calloc(1, sizeof(ndspWaveBuf));
|
|
||||||
|
|
||||||
waveBuf->data_vaddr = stream->nextData;
|
|
||||||
waveBuf->nsamples = chunkNsamples;
|
|
||||||
waveBuf->looping = false;
|
|
||||||
|
|
||||||
DSP_FlushDataCache((u32*)stream->nextData, chunkSize);
|
|
||||||
|
|
||||||
ndspChnWaveBufAdd(i, waveBuf);
|
|
||||||
|
|
||||||
stream->nextWaveBuf = waveBuf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free the last chunk if it's no longer played
|
|
||||||
if (stream->prevWaveBuf != NULL && ndspChnGetWaveBufSeq(i) != stream->prevWaveBuf->sequence_id) {
|
|
||||||
free(stream->prevWaveBuf);
|
|
||||||
stream->prevWaveBuf = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're done
|
|
||||||
if (stream->prevWaveBuf == NULL && stream->nextWaveBuf != NULL && ndspChnGetWaveBufSeq(i) != stream->nextWaveBuf->sequence_id && stream->eof) {
|
|
||||||
free(stream->nextWaveBuf);
|
|
||||||
stream->nextWaveBuf = NULL;
|
|
||||||
|
|
||||||
// Free memory
|
|
||||||
if (!stream->loop) {
|
|
||||||
linearFree(stream->prevData);
|
|
||||||
stream->prevData = NULL;
|
|
||||||
linearFree(stream->nextData);
|
|
||||||
stream->nextData = NULL;
|
|
||||||
stream->done = true;
|
|
||||||
// Loop: goto start
|
|
||||||
} else {
|
|
||||||
// Send & play audio initial data
|
|
||||||
ndspWaveBuf* waveBuf = calloc(1, sizeof(ndspWaveBuf));
|
|
||||||
|
|
||||||
waveBuf->data_vaddr = audio->data;
|
|
||||||
waveBuf->nsamples = audio->chunkNsamples;
|
|
||||||
waveBuf->looping = false;
|
|
||||||
|
|
||||||
DSP_FlushDataCache((u32*)audio->data, audio->chunkSize);
|
|
||||||
|
|
||||||
ndspChnWaveBufAdd(i, waveBuf);
|
|
||||||
|
|
||||||
stream->nextWaveBuf = waveBuf;
|
|
||||||
|
|
||||||
// Reset stream values
|
|
||||||
stream->prevStartTime = 0;
|
|
||||||
stream->eof = false;
|
|
||||||
if (audio->type == TYPE_OGG) {
|
|
||||||
stream->currentSection = audio->currentSection;
|
|
||||||
stream->rawPosition = audio->rawPosition;
|
|
||||||
} else if (audio->type == TYPE_WAV) stream->filePosition = audio->filePosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
audio object
|
audio object
|
||||||
@section Methods
|
@section Methods
|
||||||
|
|
@ -754,11 +505,8 @@ static int audio_object_time(lua_State *L) {
|
||||||
|
|
||||||
if (channel == -1 || channels[channel] != audio || !isAudioInitialized) // audio not playing
|
if (channel == -1 || channels[channel] != audio || !isAudioInitialized) // audio not playing
|
||||||
lua_pushnumber(L, 0);
|
lua_pushnumber(L, 0);
|
||||||
else {
|
else
|
||||||
double additionnalTime = 0;
|
lua_pushnumber(L, (double)(ndspChnGetSamplePos(channel)) / audio->rate);
|
||||||
if (streaming[channel] != NULL) additionnalTime = streaming[channel]->prevStartTime;
|
|
||||||
lua_pushnumber(L, (double)(ndspChnGetSamplePos(channel)) / audio->rate + additionnalTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -894,7 +642,7 @@ static int audio_object_play(lua_State *L) {
|
||||||
if (channel < 0 || channel > 23) luaL_error(L, "channel number must be between 0 and 23");
|
if (channel < 0 || channel > 23) luaL_error(L, "channel number must be between 0 and 23");
|
||||||
|
|
||||||
// Set channel parameters
|
// Set channel parameters
|
||||||
stopAudio(channel);
|
ndspChnWaveBufClear(channel);
|
||||||
ndspChnReset(channel);
|
ndspChnReset(channel);
|
||||||
ndspChnInitParams(channel);
|
ndspChnInitParams(channel);
|
||||||
ndspChnSetMix(channel, audio->mix);
|
ndspChnSetMix(channel, audio->mix);
|
||||||
|
|
@ -902,47 +650,20 @@ static int audio_object_play(lua_State *L) {
|
||||||
ndspChnSetRate(channel, audio->rate * audio->speed); // maybe hackish way to set a different speed, but it works
|
ndspChnSetRate(channel, audio->rate * audio->speed); // maybe hackish way to set a different speed, but it works
|
||||||
ndspChnSetFormat(channel, NDSP_CHANNELS(audio->channels) | NDSP_ENCODING(audio->encoding));
|
ndspChnSetFormat(channel, NDSP_CHANNELS(audio->channels) | NDSP_ENCODING(audio->encoding));
|
||||||
|
|
||||||
// Send & play audio initial data
|
// Send & play audio data
|
||||||
ndspWaveBuf* waveBuf = calloc(1, sizeof(ndspWaveBuf));
|
ndspWaveBuf* waveBuf = calloc(1, sizeof(ndspWaveBuf));
|
||||||
|
|
||||||
waveBuf->data_vaddr = audio->data;
|
waveBuf->data_vaddr = audio->data;
|
||||||
waveBuf->nsamples = audio->chunkNsamples;
|
waveBuf->nsamples = audio->nsamples;
|
||||||
waveBuf->looping = (audio->chunkSize < audio->size) ? false : loop; // let ndsp loop the chunk if not streaming
|
waveBuf->looping = loop;
|
||||||
|
|
||||||
DSP_FlushDataCache((u32*)audio->data, audio->chunkSize);
|
DSP_FlushDataCache((u32*)audio->data, audio->size);
|
||||||
|
|
||||||
ndspChnWaveBufAdd(channel, waveBuf);
|
ndspChnWaveBufAdd(channel, waveBuf);
|
||||||
channels[channel] = audio;
|
channels[channel] = audio;
|
||||||
|
|
||||||
lua_pushinteger(L, channel);
|
lua_pushinteger(L, channel);
|
||||||
|
|
||||||
// Remove last audio stream
|
|
||||||
if (streaming[channel] != NULL) {
|
|
||||||
free(streaming[channel]);
|
|
||||||
streaming[channel] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream the rest of the audio
|
|
||||||
if (audio->chunkSize < audio->size) {
|
|
||||||
audio_stream* stream = calloc(1, sizeof(audio_stream));
|
|
||||||
stream->audio = audio;
|
|
||||||
stream->loop = loop;
|
|
||||||
stream->nextWaveBuf = waveBuf;
|
|
||||||
|
|
||||||
// Allocate buffers
|
|
||||||
if (linearSpaceFree() < audio->chunkSize*2) luaL_error(L, "not enough linear memory available");
|
|
||||||
stream->nextData = linearAlloc(audio->chunkSize);
|
|
||||||
stream->prevData = linearAlloc(audio->chunkSize);
|
|
||||||
|
|
||||||
// Init stream values
|
|
||||||
if (audio->type == TYPE_OGG) {
|
|
||||||
stream->currentSection = audio->currentSection;
|
|
||||||
stream->rawPosition = audio->rawPosition;
|
|
||||||
} else if (audio->type == TYPE_WAV) stream->filePosition = audio->filePosition;
|
|
||||||
|
|
||||||
streaming[channel] = stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -967,7 +688,7 @@ static int audio_object_stop(lua_State *L) {
|
||||||
if (channel == -1) {
|
if (channel == -1) {
|
||||||
for (int i = 0; i <= 23; i++) {
|
for (int i = 0; i <= 23; i++) {
|
||||||
if (channels[i] == audio && ndspChnIsPlaying(i)) {
|
if (channels[i] == audio && ndspChnIsPlaying(i)) {
|
||||||
stopAudio(i);
|
ndspChnWaveBufClear(i);
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -975,7 +696,7 @@ static int audio_object_stop(lua_State *L) {
|
||||||
luaL_error(L, "channel number must be between 0 and 23");
|
luaL_error(L, "channel number must be between 0 and 23");
|
||||||
} else {
|
} else {
|
||||||
if (channels[channel] == audio && ndspChnIsPlaying(channel)) {
|
if (channels[channel] == audio && ndspChnIsPlaying(channel)) {
|
||||||
stopAudio(channel);
|
ndspChnWaveBufClear(channel);
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -988,7 +709,7 @@ static int audio_object_stop(lua_State *L) {
|
||||||
/***
|
/***
|
||||||
Returns the audio object type.
|
Returns the audio object type.
|
||||||
@function :type
|
@function :type
|
||||||
@treturn string "ogg", "wav" or "raw"
|
@treturn string "ogg" or "wav"
|
||||||
*/
|
*/
|
||||||
static int audio_object_type(lua_State *L) {
|
static int audio_object_type(lua_State *L) {
|
||||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||||
|
|
@ -997,10 +718,6 @@ static int audio_object_type(lua_State *L) {
|
||||||
lua_pushstring(L, "ogg");
|
lua_pushstring(L, "ogg");
|
||||||
else if (audio->type == TYPE_WAV)
|
else if (audio->type == TYPE_WAV)
|
||||||
lua_pushstring(L, "wav");
|
lua_pushstring(L, "wav");
|
||||||
else if (audio->type == TYPE_RAW)
|
|
||||||
lua_pushstring(L, "raw");
|
|
||||||
else
|
|
||||||
lua_pushstring(L, "unknown");
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -1016,13 +733,12 @@ static int audio_object_unload(lua_State *L) {
|
||||||
if (isAudioInitialized) {
|
if (isAudioInitialized) {
|
||||||
for (int i = 0; i <= 23; i++) {
|
for (int i = 0; i <= 23; i++) {
|
||||||
if (channels[i] == audio) {
|
if (channels[i] == audio) {
|
||||||
stopAudio(i);
|
ndspChnWaveBufClear(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio->type == TYPE_OGG) ov_clear(&audio->vf);
|
if (audio->type == TYPE_OGG) ov_clear(&audio->vf);
|
||||||
else if (audio->type == TYPE_WAV) fclose(audio->file);
|
|
||||||
|
|
||||||
// Free memory
|
// Free memory
|
||||||
linearFree(audio->data);
|
linearFree(audio->data);
|
||||||
|
|
@ -1156,7 +872,6 @@ static const struct luaL_Reg audio_lib[] = {
|
||||||
{ "interpolation", audio_interpolation },
|
{ "interpolation", audio_interpolation },
|
||||||
{ "speed", audio_speed },
|
{ "speed", audio_speed },
|
||||||
{ "stop", audio_stop },
|
{ "stop", audio_stop },
|
||||||
{ "update", audio_update },
|
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1172,7 +887,9 @@ int luaopen_audio_lib(lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_audio_lib(lua_State *L) {
|
void load_audio_lib(lua_State *L) {
|
||||||
if (!isAudioInitialized) isAudioInitialized = !ndspInit(); // ndspInit returns 0 in case of success
|
if (!isAudioInitialized) {
|
||||||
|
isAudioInitialized = !ndspInit(); // ndspInit returns 0 in case of success
|
||||||
|
}
|
||||||
|
|
||||||
luaL_requiref(L, "ctr.audio", luaopen_audio_lib, false);
|
luaL_requiref(L, "ctr.audio", luaopen_audio_lib, false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,6 @@ The `cam` module.
|
||||||
/***
|
/***
|
||||||
Initialize the camera module.
|
Initialize the camera module.
|
||||||
@function init
|
@function init
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int cam_init(lua_State *L) {
|
static int cam_init(lua_State *L) {
|
||||||
Result ret = camInit();
|
Result ret = camInit();
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,13 @@ Used to get some user config.
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
||||||
bool initStateCFGU = false;
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Initialize the CFGU module.
|
Initialize the CFGU module.
|
||||||
@function init
|
@function init
|
||||||
*/
|
*/
|
||||||
static int cfgu_init(lua_State *L) {
|
static int cfgu_init(lua_State *L) {
|
||||||
if (!initStateCFGU) {
|
|
||||||
cfguInit();
|
cfguInit();
|
||||||
initStateCFGU = true;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,10 +29,8 @@ Disable the CFGU module.
|
||||||
@function shutdown
|
@function shutdown
|
||||||
*/
|
*/
|
||||||
static int cfgu_shutdown(lua_State *L) {
|
static int cfgu_shutdown(lua_State *L) {
|
||||||
if (initStateCFGU) {
|
|
||||||
cfguExit();
|
cfguExit();
|
||||||
initStateCFGU = false;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,13 +103,9 @@ static int cfgu_getUsername(lua_State *L) {
|
||||||
|
|
||||||
CFGU_GetConfigInfoBlk2(0x1C, 0xA0000, (u8*)block);
|
CFGU_GetConfigInfoBlk2(0x1C, 0xA0000, (u8*)block);
|
||||||
u8 *name = malloc(0x14);
|
u8 *name = malloc(0x14);
|
||||||
ssize_t len = utf16_to_utf8(name, block, 0x14);
|
utf16_to_utf8(name, block, 0x14);
|
||||||
if (len < 0) {
|
|
||||||
lua_pushstring(L, "");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushlstring(L, (const char *)name, len); // The username is only 0x14 characters long.
|
lua_pushlstring(L, (const char *)name, 0x14); // The username is only 0x14 characters long.
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,10 +309,3 @@ int luaopen_cfgu_lib(lua_State *L) {
|
||||||
void load_cfgu_lib(lua_State *L) {
|
void load_cfgu_lib(lua_State *L) {
|
||||||
luaL_requiref(L, "ctr.cfgu", luaopen_cfgu_lib, false);
|
luaL_requiref(L, "ctr.cfgu", luaopen_cfgu_lib, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unload_cfgu_lib(lua_State *L) {
|
|
||||||
if (initStateCFGU) {
|
|
||||||
initStateCFGU = false;
|
|
||||||
cfguExit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
67
source/ctr.c
67
source/ctr.c
|
|
@ -3,9 +3,6 @@ The `ctr` module.
|
||||||
@module ctr
|
@module ctr
|
||||||
@usage local ctr = require("ctr")
|
@usage local ctr = require("ctr")
|
||||||
*/
|
*/
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/services/apt.h>
|
#include <3ds/services/apt.h>
|
||||||
#include <3ds/os.h>
|
#include <3ds/os.h>
|
||||||
|
|
@ -28,7 +25,6 @@ The `ctr.news` module.
|
||||||
@see ctr.news
|
@see ctr.news
|
||||||
*/
|
*/
|
||||||
void load_news_lib(lua_State *L);
|
void load_news_lib(lua_State *L);
|
||||||
void unload_news_lib(lua_State *L);
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
The `ctr.ptm` module.
|
The `ctr.ptm` module.
|
||||||
|
|
@ -36,7 +32,6 @@ The `ctr.ptm` module.
|
||||||
@see ctr.ptm
|
@see ctr.ptm
|
||||||
*/
|
*/
|
||||||
void load_ptm_lib(lua_State *L);
|
void load_ptm_lib(lua_State *L);
|
||||||
void unload_ptm_lib(lua_State *L);
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
The `ctr.hid` module.
|
The `ctr.hid` module.
|
||||||
|
|
@ -82,7 +77,6 @@ The `ctr.cfgu` module.
|
||||||
@see ctr.cfgu
|
@see ctr.cfgu
|
||||||
*/
|
*/
|
||||||
void load_cfgu_lib(lua_State *L);
|
void load_cfgu_lib(lua_State *L);
|
||||||
void unload_cfgu_lib(lua_State *L);
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
The `ctr.socket` module.
|
The `ctr.socket` module.
|
||||||
|
|
@ -112,7 +106,6 @@ The `ctr.apt` module.
|
||||||
@see ctr.apt
|
@see ctr.apt
|
||||||
*/
|
*/
|
||||||
void load_apt_lib(lua_State *L);
|
void load_apt_lib(lua_State *L);
|
||||||
void unload_apt_lib(lua_State *L);
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
The `ctr.mic` module.
|
The `ctr.mic` module.
|
||||||
|
|
@ -128,14 +121,6 @@ The `ctr.thread` module.
|
||||||
*/
|
*/
|
||||||
void load_thread_lib(lua_State *L);
|
void load_thread_lib(lua_State *L);
|
||||||
|
|
||||||
/***
|
|
||||||
The `ctr.uds` module.
|
|
||||||
@table uds
|
|
||||||
@see ctr.uds
|
|
||||||
*/
|
|
||||||
void load_uds_lib(lua_State *L);
|
|
||||||
void unload_uds_lib(lua_State *L);
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Return whether or not the program should continue.
|
Return whether or not the program should continue.
|
||||||
@function run
|
@function run
|
||||||
|
|
@ -148,23 +133,11 @@ static int ctr_run(lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Return the number of milliseconds spent since some point in time.
|
Return the number of milliseconds since 1st Jan 1900 00:00.
|
||||||
This can be used to measure a duration with milliseconds precision; however this can't be used to get the current time or date.
|
|
||||||
See Lua's os.date() for this use.
|
|
||||||
For various reasons (see the C source), this will actually returns a negative value.
|
|
||||||
@function time
|
@function time
|
||||||
@treturn number milliseconds
|
@treturn number milliseconds
|
||||||
@usage
|
|
||||||
-- Measuring a duration:
|
|
||||||
local startTime = ctr.time()
|
|
||||||
-- do stuff
|
|
||||||
local duration = ctr.time() - startTime
|
|
||||||
*/
|
*/
|
||||||
static int ctr_time(lua_State *L) {
|
static int ctr_time(lua_State *L) {
|
||||||
// osGetTime actually returns the number of seconds elapsed since 1st Jan 1900 00:00.
|
|
||||||
// However, it returns a u64, we build Lua with 32bits numbers, and every number is signed in Lua, so this obvioulsy doesn't work
|
|
||||||
// and actually returns a negative value. It still works for durations however. Because having the date and time with millisecond-presion
|
|
||||||
// doesn't really seem useful and changing ctrµLua's API to work on 64bits numbers will take a long time, we choosed to keep this as-is.
|
|
||||||
lua_pushinteger(L, osGetTime());
|
lua_pushinteger(L, osGetTime());
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -192,22 +165,21 @@ static const struct luaL_Reg ctr_lib[] = {
|
||||||
// Subtables
|
// Subtables
|
||||||
struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } ctr_libs[] = {
|
struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } ctr_libs[] = {
|
||||||
{ "gfx", load_gfx_lib, unload_gfx_lib },
|
{ "gfx", load_gfx_lib, unload_gfx_lib },
|
||||||
{ "news", load_news_lib, unload_news_lib },
|
{ "news", load_news_lib, NULL },
|
||||||
{ "ptm", load_ptm_lib, unload_ptm_lib },
|
{ "ptm", load_ptm_lib, NULL },
|
||||||
{ "hid", load_hid_lib, unload_hid_lib },
|
{ "hid", load_hid_lib, unload_hid_lib },
|
||||||
{ "ir", load_ir_lib, NULL },
|
{ "ir", load_ir_lib, NULL },
|
||||||
{ "fs", load_fs_lib, unload_fs_lib },
|
{ "fs", load_fs_lib, unload_fs_lib },
|
||||||
{ "httpc", load_httpc_lib, unload_httpc_lib },
|
{ "httpc", load_httpc_lib, unload_httpc_lib },
|
||||||
{ "qtm", load_qtm_lib, NULL },
|
{ "qtm", load_qtm_lib, NULL },
|
||||||
{ "cfgu", load_cfgu_lib, unload_cfgu_lib },
|
{ "cfgu", load_cfgu_lib, NULL },
|
||||||
{ "socket", load_socket_lib, NULL },
|
{ "socket", load_socket_lib, NULL },
|
||||||
{ "cam", load_cam_lib, NULL },
|
{ "cam", load_cam_lib, NULL },
|
||||||
{ "audio", load_audio_lib, unload_audio_lib },
|
{ "audio", load_audio_lib, unload_audio_lib },
|
||||||
{ "apt", load_apt_lib, unload_apt_lib },
|
{ "apt", load_apt_lib, NULL },
|
||||||
{ "mic", load_mic_lib, NULL },
|
{ "mic", load_mic_lib, NULL },
|
||||||
{ "thread", load_thread_lib, NULL },
|
{ "thread", load_thread_lib, NULL },
|
||||||
{ "uds", load_uds_lib, unload_uds_lib },
|
{ NULL, NULL }
|
||||||
{ NULL, NULL, NULL }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int luaopen_ctr_lib(lua_State *L) {
|
int luaopen_ctr_lib(lua_State *L) {
|
||||||
|
|
@ -218,33 +190,6 @@ int luaopen_ctr_lib(lua_State *L) {
|
||||||
lua_setfield(L, -2, ctr_libs[i].name);
|
lua_setfield(L, -2, ctr_libs[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Running version of ctrµLua. This string contains the exact name of the last (pre-)release tag.
|
|
||||||
@field version
|
|
||||||
*/
|
|
||||||
lua_pushstring(L, CTR_VERSION);
|
|
||||||
lua_setfield(L, -2, "version");
|
|
||||||
/***
|
|
||||||
Running build of ctrµLua. This string contains the last commit hash.
|
|
||||||
@field build
|
|
||||||
*/
|
|
||||||
lua_pushstring(L, CTR_BUILD);
|
|
||||||
lua_setfield(L, -2, "build");
|
|
||||||
|
|
||||||
/***
|
|
||||||
Root directory of ctrµLua. Contains the working directory where ctrµLua has been launched OR the romfs root if romfs has been enabled.
|
|
||||||
@field root
|
|
||||||
*/
|
|
||||||
#ifdef ROMFS
|
|
||||||
char* buff = "romfs:/";
|
|
||||||
chdir(buff);
|
|
||||||
#else
|
|
||||||
char* buff = malloc(1024);
|
|
||||||
getcwd(buff, 1024);
|
|
||||||
#endif
|
|
||||||
lua_pushstring(L, buff);
|
|
||||||
lua_setfield(L, -2, "root");
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/***
|
/***
|
||||||
The `gfx.font` module
|
The `font` module
|
||||||
@module ctr.gfx.font
|
@module ctr.gfx.font
|
||||||
@usage local font = require("ctr.gfx.font")
|
@usage local font = require("ctr.gfx.font")
|
||||||
*/
|
*/
|
||||||
|
|
@ -15,16 +15,11 @@ The `gfx.font` module
|
||||||
|
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
|
|
||||||
u32 textSize = 9;
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Load a font. Supported formats: TTF, OTF, TTC, OTC, WOFF, PFA, PFB, PCF, FNT, BDF, PFR, and others.
|
Load a TTF font.
|
||||||
ctrµLua support all formats supported by FreeType. See here for a more complete list: http://freetype.org/freetype2/docs/index.html
|
|
||||||
@function load
|
@function load
|
||||||
@tparam string path path to the file
|
@tparam string path path to the file
|
||||||
@treturn[1] font the loaded font.
|
@treturn font the loaded font.
|
||||||
@treturn[2] nil if an error occurred
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
*/
|
||||||
static int font_load(lua_State *L) {
|
static int font_load(lua_State *L) {
|
||||||
const char *path = luaL_checkstring(L, 1);
|
const char *path = luaL_checkstring(L, 1);
|
||||||
|
|
@ -75,28 +70,6 @@ static int font_getDefault(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Set the default text size.
|
|
||||||
@function setSize
|
|
||||||
@tparam number size new default text size
|
|
||||||
*/
|
|
||||||
static int font_setSize(lua_State *L) {
|
|
||||||
textSize = luaL_checkinteger(L, 1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Return the default text size.
|
|
||||||
@function getSize
|
|
||||||
@treturn number the default text size
|
|
||||||
*/
|
|
||||||
static int font_getSize(lua_State *L) {
|
|
||||||
lua_pushinteger(L, textSize);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
font object
|
font object
|
||||||
@section Methods
|
@section Methods
|
||||||
|
|
@ -106,7 +79,6 @@ font object
|
||||||
Return the width of a string with a font.
|
Return the width of a string with a font.
|
||||||
@function :width
|
@function :width
|
||||||
@tparam string text the text to test
|
@tparam string text the text to test
|
||||||
@tparam[opt=default size] integer font size, in pixels
|
|
||||||
@treturn number the width of the text (in pixels)
|
@treturn number the width of the text (in pixels)
|
||||||
*/
|
*/
|
||||||
static int font_object_width(lua_State *L) {
|
static int font_object_width(lua_State *L) {
|
||||||
|
|
@ -123,7 +95,7 @@ static int font_object_width(lua_State *L) {
|
||||||
len = mbstowcs(wtext, text, len);
|
len = mbstowcs(wtext, text, len);
|
||||||
*(wtext+len) = 0x0; // text end
|
*(wtext+len) = 0x0; // text end
|
||||||
|
|
||||||
lua_pushinteger(L, sftd_get_wtext_width(font->font, size, wtext));
|
lua_pushinteger(L, sftd_width_wtext(font->font, size, wtext));
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -155,8 +127,6 @@ static const struct luaL_Reg font_lib[] = {
|
||||||
{ "load", font_load },
|
{ "load", font_load },
|
||||||
{ "setDefault", font_setDefault },
|
{ "setDefault", font_setDefault },
|
||||||
{ "getDefault", font_getDefault },
|
{ "getDefault", font_getDefault },
|
||||||
{ "setSize", font_setSize },
|
|
||||||
{ "getSize", font_getSize },
|
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -190,6 +160,4 @@ void unload_font_lib(lua_State *L) {
|
||||||
|
|
||||||
if (luaL_testudata(L, -1, "LFont") != NULL)
|
if (luaL_testudata(L, -1, "LFont") != NULL)
|
||||||
sftd_free_font(((font_userdata *)lua_touserdata(L, -1))->font); // Unload current font
|
sftd_free_font(((font_userdata *)lua_touserdata(L, -1))->font); // Unload current font
|
||||||
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,4 @@ typedef struct {
|
||||||
sftd_font *font;
|
sftd_font *font;
|
||||||
} font_userdata;
|
} font_userdata;
|
||||||
|
|
||||||
extern u32 textSize;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
145
source/fs.c
145
source/fs.c
|
|
@ -3,24 +3,25 @@ The `fs` module.
|
||||||
@module ctr.fs
|
@module ctr.fs
|
||||||
@usage local fs = require("ctr.fs")
|
@usage local fs = require("ctr.fs")
|
||||||
*/
|
*/
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
|
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/util/utf.h>
|
#include <3ds/util/utf.h>
|
||||||
#include <3ds/services/fs.h>
|
#include <3ds/services/fs.h>
|
||||||
#include <3ds/sdmc.h>
|
|
||||||
#include <3ds/romfs.h>
|
|
||||||
|
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
||||||
bool isFsInitialized = false;
|
bool isFsInitialized = false;
|
||||||
|
|
||||||
|
Handle *fsuHandle;
|
||||||
|
FS_Archive sdmcArchive;
|
||||||
|
#ifdef ROMFS
|
||||||
|
FS_Archive romfsArchive;
|
||||||
|
#endif
|
||||||
|
|
||||||
/***
|
/***
|
||||||
The `ctr.fs.lzlib` module.
|
The `ctr.fs.lzlib` module.
|
||||||
@table lzlib
|
@table lzlib
|
||||||
|
|
@ -39,7 +40,7 @@ const char* prefix_path(const char* path) {
|
||||||
char* prefix = "sdmc:";
|
char* prefix = "sdmc:";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char out[1024];
|
char out[256];
|
||||||
strcpy(out, prefix);
|
strcpy(out, prefix);
|
||||||
return strcat(out, path);
|
return strcat(out, path);
|
||||||
|
|
||||||
|
|
@ -52,78 +53,85 @@ const char* prefix_path(const char* path) {
|
||||||
Lists a directory contents (unsorted).
|
Lists a directory contents (unsorted).
|
||||||
@function list
|
@function list
|
||||||
@tparam string path the directory we wants to list the content
|
@tparam string path the directory we wants to list the content
|
||||||
@treturn[1] table the item list. Each item is a table like:
|
@treturn table the item list. Each item is a table like:
|
||||||
`
|
`
|
||||||
{
|
{
|
||||||
name = "Item name.txt",
|
name = "Item name.txt",
|
||||||
|
shortName = "ITEM~",
|
||||||
|
shortExt = "TXT",
|
||||||
isDirectory = false,
|
isDirectory = false,
|
||||||
size = 321 -- (integer) item size, in bytes
|
isHidden = false,
|
||||||
|
isArchive = false,
|
||||||
|
isReadOnly = false,
|
||||||
|
fileSize = 321 -- (integer) in bytes
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@treturn[2] nil if an error occurred
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
*/
|
||||||
static int fs_list(lua_State *L) {
|
static int fs_list(lua_State *L) {
|
||||||
const char* basepath = prefix_path(luaL_checkstring(L, 1));
|
const char *path = prefix_path(luaL_checkstring(L, 1));
|
||||||
char* path;
|
|
||||||
bool shouldFreePath = false;
|
|
||||||
if (basepath[strlen(basepath)-1] != '/') {
|
|
||||||
path = malloc(strlen(basepath)+2);
|
|
||||||
strcpy(path, basepath);
|
|
||||||
strcat(path, "/");
|
|
||||||
shouldFreePath = true;
|
|
||||||
} else {
|
|
||||||
path = (char*)basepath;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
int i = 1; // table index
|
int i = 1; // table index
|
||||||
|
|
||||||
DIR* dir = opendir(path);
|
// Get default archive
|
||||||
if (dir == NULL) {
|
#ifdef ROMFS
|
||||||
if (shouldFreePath) free(path);
|
FS_Archive archive = romfsArchive;
|
||||||
lua_pushnil(L);
|
#else
|
||||||
lua_pushfstring(L, "Can't open directory: %s (%s)", strerror(errno), errno);
|
FS_Archive archive = sdmcArchive;
|
||||||
return 2;
|
#endif
|
||||||
|
// Archive path override (and skip path prefix)
|
||||||
|
if (strncmp(path, "sdmc:", 5) == 0) {
|
||||||
|
path += 5;
|
||||||
|
archive = sdmcArchive;
|
||||||
|
#ifdef ROMFS
|
||||||
|
} else if (strncmp(path, "romfs:", 6) == 0) {
|
||||||
|
path += 6;
|
||||||
|
archive = romfsArchive;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
errno = 0;
|
|
||||||
struct dirent *entry;
|
|
||||||
while (((entry = readdir(dir)) != NULL) && !errno) {
|
|
||||||
lua_createtable(L, 0, 3);
|
|
||||||
|
|
||||||
lua_pushstring(L, (const char*)entry->d_name);
|
FS_Path dirPath = fsMakePath(PATH_ASCII, path);
|
||||||
|
|
||||||
|
Handle dirHandle;
|
||||||
|
FSUSER_OpenDirectory(&dirHandle, archive, dirPath);
|
||||||
|
|
||||||
|
u32 entriesRead = 0;
|
||||||
|
do {
|
||||||
|
FS_DirectoryEntry buffer;
|
||||||
|
|
||||||
|
FSDIR_Read(dirHandle, &entriesRead, 1, &buffer);
|
||||||
|
|
||||||
|
if (!entriesRead) break;
|
||||||
|
|
||||||
|
uint8_t name[0x106+1]; // utf8 file name
|
||||||
|
size_t size = utf16_to_utf8(name, buffer.name, 0x106);
|
||||||
|
*(name+size) = 0x0; // mark text end
|
||||||
|
|
||||||
|
lua_createtable(L, 0, 8);
|
||||||
|
|
||||||
|
lua_pushstring(L, (const char *)name);
|
||||||
lua_setfield(L, -2, "name");
|
lua_setfield(L, -2, "name");
|
||||||
lua_pushboolean(L, entry->d_type==DT_DIR);
|
lua_pushstring(L, (const char *)buffer.shortName);
|
||||||
|
lua_setfield(L, -2, "shortName");
|
||||||
|
lua_pushstring(L, (const char *)buffer.shortExt);
|
||||||
|
lua_setfield(L, -2, "shortExt");
|
||||||
|
lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_DIRECTORY);
|
||||||
lua_setfield(L, -2, "isDirectory");
|
lua_setfield(L, -2, "isDirectory");
|
||||||
|
lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_HIDDEN);
|
||||||
if (entry->d_type==DT_REG) { // Regular files: check size
|
lua_setfield(L, -2, "isHidden");
|
||||||
char* filepath = malloc(strlen(path)+strlen(entry->d_name)+1);
|
lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_ARCHIVE);
|
||||||
if (filepath == NULL)
|
lua_setfield(L, -2, "isArchive");
|
||||||
luaL_error(L, "Memory allocation error");
|
lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_READ_ONLY);
|
||||||
strcpy(filepath, path);
|
lua_setfield(L, -2, "isReadOnly");
|
||||||
strcat(filepath, entry->d_name);
|
lua_pushinteger(L, buffer.fileSize);
|
||||||
|
lua_setfield(L, -2, "fileSize");
|
||||||
struct stat stats;
|
|
||||||
if (stat(filepath, &stats)) {
|
|
||||||
free(filepath);
|
|
||||||
if (shouldFreePath) free(path);
|
|
||||||
luaL_error(L, "Stat error: %s (%d)", strerror(errno), errno);
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
lua_pushinteger(L, stats.st_size);
|
|
||||||
}
|
|
||||||
free(filepath);
|
|
||||||
} else { // Everything else: 0 bytes
|
|
||||||
lua_pushinteger(L, 0);
|
|
||||||
}
|
|
||||||
lua_setfield(L, -2, "size");
|
|
||||||
|
|
||||||
lua_seti(L, -2, i);
|
lua_seti(L, -2, i);
|
||||||
i++;
|
i++;
|
||||||
}
|
|
||||||
|
|
||||||
closedir(dir);
|
} while (entriesRead > 0);
|
||||||
if (shouldFreePath) free(path);
|
|
||||||
|
FSDIR_Close(dirHandle);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -148,9 +156,9 @@ Get the current working directory.
|
||||||
@treturn string the current working directory
|
@treturn string the current working directory
|
||||||
*/
|
*/
|
||||||
static int fs_getDirectory(lua_State *L) {
|
static int fs_getDirectory(lua_State *L) {
|
||||||
char cwd[1024];
|
char cwd[256];
|
||||||
|
|
||||||
lua_pushstring(L, getcwd(cwd, 1024));
|
lua_pushstring(L, getcwd(cwd, 256));
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -206,9 +214,16 @@ int luaopen_fs_lib(lua_State *L) {
|
||||||
|
|
||||||
void load_fs_lib(lua_State *L) {
|
void load_fs_lib(lua_State *L) {
|
||||||
if (!isFsInitialized) {
|
if (!isFsInitialized) {
|
||||||
sdmcInit();
|
fsInit();
|
||||||
|
|
||||||
|
fsuHandle = fsGetSessionHandle();
|
||||||
|
FSUSER_Initialize(*fsuHandle);
|
||||||
|
|
||||||
|
sdmcArchive = (FS_Archive){ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")};
|
||||||
|
FSUSER_OpenArchive(&sdmcArchive);
|
||||||
#ifdef ROMFS
|
#ifdef ROMFS
|
||||||
romfsInit();
|
romfsArchive = (FS_Archive){ARCHIVE_ROMFS, fsMakePath(PATH_EMPTY, "")};
|
||||||
|
FSUSER_OpenArchive(&romfsArchive);
|
||||||
#endif
|
#endif
|
||||||
isFsInitialized = true;
|
isFsInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
@ -217,8 +232,10 @@ void load_fs_lib(lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void unload_fs_lib(lua_State *L) {
|
void unload_fs_lib(lua_State *L) {
|
||||||
sdmcExit();
|
FSUSER_CloseArchive(&sdmcArchive);
|
||||||
#ifdef ROMFS
|
#ifdef ROMFS
|
||||||
romfsExit();
|
FSUSER_CloseArchive(&romfsArchive);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
fsExit();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
488
source/gfx.c
488
source/gfx.c
|
|
@ -4,45 +4,21 @@ The `gfx` module.
|
||||||
@usage local gfx = require("ctr.gfx")
|
@usage local gfx = require("ctr.gfx")
|
||||||
*/
|
*/
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include <sf2d.h>
|
#include <sf2d.h>
|
||||||
#include <sftd.h>
|
#include <sftd.h>
|
||||||
|
|
||||||
//#include <3ds/vram.h>
|
#include <3ds/vram.h>
|
||||||
//#include <3ds/services/gsp.h>
|
//#include <3ds/services/gsp.h>
|
||||||
#include <3ds/console.h>
|
|
||||||
|
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
||||||
#include "gfx.h"
|
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "texture.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
sf2d_rendertarget *target;
|
|
||||||
} target_userdata;
|
|
||||||
|
|
||||||
bool isGfxInitialized = false;
|
bool isGfxInitialized = false;
|
||||||
bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib.
|
bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib.
|
||||||
|
|
||||||
// The scissor-test state, as defined in Lua code. When you apply a new scissor in C, remember to get back to this state to avoid unexpected behaviour.
|
|
||||||
scissor_state lua_scissor = {
|
|
||||||
GPU_SCISSOR_DISABLE,
|
|
||||||
0, 0,
|
|
||||||
0, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// Rotate a point (x,y) around the center (cx,cy) by angle radians.
|
|
||||||
void rotatePoint(int x, int y, int cx, int cy, float angle, int* outx, int* outy) {
|
|
||||||
float s = sin(angle), c = cos(angle);
|
|
||||||
int tx = x - cx, ty = y - cy;
|
|
||||||
*outx = round(tx * c - ty * s) + cx;
|
|
||||||
*outy = round(tx * s + ty * c) + cy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
The `ctr.gfx.color` module.
|
The `ctr.gfx.color` module.
|
||||||
@table color
|
@table color
|
||||||
|
|
@ -74,23 +50,17 @@ The `ctr.gfx.map` module.
|
||||||
void load_map_lib(lua_State *L);
|
void load_map_lib(lua_State *L);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Start drawing to a screen/target.
|
Start drawing to a screen.
|
||||||
Must be called before any draw operation.
|
Must be called before any draw operation.
|
||||||
@function start
|
@function start
|
||||||
@tparam number/target screen the screen or target to draw to (`gfx.TOP`, `gfx.BOTTOM`, or render target)
|
@tparam number screen the screen to draw to (`gfx.TOP` or `gfx.BOTTOM`)
|
||||||
@tparam[opt=gfx.LEFT] number eye the eye to draw to (`gfx.LEFT` or `gfx.RIGHT`)
|
@tparam[opt=gfx.LEFT] number eye the eye to draw to (`gfx.LEFT` or `gfx.RIGHT`)
|
||||||
*/
|
*/
|
||||||
static int gfx_start(lua_State *L) {
|
static int gfx_start(lua_State *L) {
|
||||||
if (lua_isinteger(L, 1)) {
|
|
||||||
u8 screen = luaL_checkinteger(L, 1);
|
u8 screen = luaL_checkinteger(L, 1);
|
||||||
u8 eye = luaL_optinteger(L, 2, GFX_LEFT);
|
u8 eye = luaL_optinteger(L, 2, GFX_LEFT);
|
||||||
|
|
||||||
sf2d_start_frame(screen, eye);
|
sf2d_start_frame(screen, eye);
|
||||||
} else if (lua_isuserdata(L, 1)) {
|
|
||||||
target_userdata *target = luaL_checkudata(L, 1, "LTarget");
|
|
||||||
|
|
||||||
sf2d_start_frame_target(target->target);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -192,6 +162,28 @@ static int gfx_vramSpaceFree(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
Draw a line on the current screen.
|
||||||
|
@function line
|
||||||
|
@tparam integer x1 line's starting point horizontal coordinate, in pixels
|
||||||
|
@tparam integer y1 line's starting point vertical coordinate, in pixels
|
||||||
|
@tparam integer x2 line's endpoint horizontal coordinate, in pixels
|
||||||
|
@tparam integer y2 line's endpoint vertical coordinate, in pixels
|
||||||
|
@tparam[opt=default color] integer color drawing color
|
||||||
|
*/
|
||||||
|
static int gfx_line(lua_State *L) {
|
||||||
|
int x1 = luaL_checkinteger(L, 1);
|
||||||
|
int y1 = luaL_checkinteger(L, 2);
|
||||||
|
int x2 = luaL_checkinteger(L, 3);
|
||||||
|
int y2 = luaL_checkinteger(L, 4);
|
||||||
|
|
||||||
|
u32 color = luaL_optinteger(L, 5, color_default);
|
||||||
|
|
||||||
|
sf2d_draw_line(x1, y1, x2, y2, color);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Draw a point, a single pixel, on the current screen.
|
Draw a point, a single pixel, on the current screen.
|
||||||
@function point
|
@function point
|
||||||
|
|
@ -211,87 +203,7 @@ static int gfx_point(lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Draw a line on the current screen.
|
Draw a rectangle on the current screen.
|
||||||
@function line
|
|
||||||
@tparam integer x1 line's starting point horizontal coordinate, in pixels
|
|
||||||
@tparam integer y1 line's starting point vertical coordinate, in pixels
|
|
||||||
@tparam integer x2 line's endpoint horizontal coordinate, in pixels
|
|
||||||
@tparam integer y2 line's endpoint vertical coordinate, in pixels
|
|
||||||
@tparam[opt=1] number width line's thickness, in pixels
|
|
||||||
@tparam[opt=default color] integer color drawing color
|
|
||||||
*/
|
|
||||||
static int gfx_line(lua_State *L) {
|
|
||||||
int x1 = luaL_checkinteger(L, 1);
|
|
||||||
int y1 = luaL_checkinteger(L, 2);
|
|
||||||
int x2 = luaL_checkinteger(L, 3);
|
|
||||||
int y2 = luaL_checkinteger(L, 4);
|
|
||||||
float width = luaL_optnumber(L, 5, 1.0f);
|
|
||||||
|
|
||||||
u32 color = luaL_optinteger(L, 6, color_default);
|
|
||||||
|
|
||||||
sf2d_draw_line(x1, y1, x2, y2, width, color);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Draw a filled triangle on the current screen.
|
|
||||||
@function triangle
|
|
||||||
@tparam integer x1 horizontal coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer y1 vertical coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer x2 horizontal coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer y2 vertical coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer x3 horizontal coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer y3 vertical coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam[opt=default color] integer color drawing color
|
|
||||||
*/
|
|
||||||
static int gfx_triangle(lua_State *L) {
|
|
||||||
int x1 = luaL_checkinteger(L, 1);
|
|
||||||
int y1 = luaL_checkinteger(L, 2);
|
|
||||||
int x2 = luaL_checkinteger(L, 3);
|
|
||||||
int y2 = luaL_checkinteger(L, 4);
|
|
||||||
int x3 = luaL_checkinteger(L, 5);
|
|
||||||
int y3 = luaL_checkinteger(L, 6);
|
|
||||||
|
|
||||||
u32 color = luaL_optinteger(L, 7, color_default);
|
|
||||||
|
|
||||||
sf2d_draw_triangle(x1, y1, x2, y2, x3, y3, color);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Draw a triangle outline on the current screen.
|
|
||||||
@function linedTriangle
|
|
||||||
@tparam integer x1 horizontal coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer y1 vertical coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer x2 horizontal coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer y2 vertical coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer x3 horizontal coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam integer y3 vertical coordinate of a vertex of the triangle, in pixels
|
|
||||||
@tparam[opt=1] number lineWidth line's thickness, in pixels
|
|
||||||
@tparam[opt=default color] integer color drawing color
|
|
||||||
*/
|
|
||||||
static int gfx_linedTriangle(lua_State *L) {
|
|
||||||
int x1 = luaL_checkinteger(L, 1);
|
|
||||||
int y1 = luaL_checkinteger(L, 2);
|
|
||||||
int x2 = luaL_checkinteger(L, 3);
|
|
||||||
int y2 = luaL_checkinteger(L, 4);
|
|
||||||
int x3 = luaL_checkinteger(L, 5);
|
|
||||||
int y3 = luaL_checkinteger(L, 6);
|
|
||||||
float lineWidth = luaL_optnumber(L, 7, 1.0f);
|
|
||||||
|
|
||||||
u32 color = luaL_optinteger(L, 8, color_default);
|
|
||||||
|
|
||||||
sf2d_draw_line(x1, y1, x2, y2, lineWidth, color);
|
|
||||||
sf2d_draw_line(x2, y2, x3, y3, lineWidth, color);
|
|
||||||
sf2d_draw_line(x3, y3, x1, y1, lineWidth, color);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Draw a filled rectangle on the current screen.
|
|
||||||
@function rectangle
|
@function rectangle
|
||||||
@tparam integer x rectangle origin horizontal coordinate, in pixels
|
@tparam integer x rectangle origin horizontal coordinate, in pixels
|
||||||
@tparam integer y rectangle origin vertical coordinate, in pixels
|
@tparam integer y rectangle origin vertical coordinate, in pixels
|
||||||
|
|
@ -299,8 +211,6 @@ Draw a filled rectangle on the current screen.
|
||||||
@tparam integer height rectangle height, in pixels
|
@tparam integer height rectangle height, in pixels
|
||||||
@tparam[opt=0] number angle rectangle rotation, in radians
|
@tparam[opt=0] number angle rectangle rotation, in radians
|
||||||
@tparam[opt=default color] integer color drawing color
|
@tparam[opt=default color] integer color drawing color
|
||||||
@tparam[opt] integer color2 Second drawing color ; if the argument is not nil, the rectangle will be filled with a gradient from color to color2
|
|
||||||
@tparam[opt] integer direction Gradient drawing direction (`gfx.TOP_TO_BOTTOM` or `gfx.LEFT_TO_RIGHT`). This argument is mandatory if a second color was specified.
|
|
||||||
*/
|
*/
|
||||||
static int gfx_rectangle(lua_State *L) {
|
static int gfx_rectangle(lua_State *L) {
|
||||||
int x = luaL_checkinteger(L, 1);
|
int x = luaL_checkinteger(L, 1);
|
||||||
|
|
@ -311,72 +221,16 @@ static int gfx_rectangle(lua_State *L) {
|
||||||
float angle = luaL_optnumber(L, 5, 0);
|
float angle = luaL_optnumber(L, 5, 0);
|
||||||
u32 color = luaL_optinteger(L, 6, color_default);
|
u32 color = luaL_optinteger(L, 6, color_default);
|
||||||
|
|
||||||
// Not second color : fill with plain color.
|
|
||||||
if (lua_isnoneornil(L, 7)) {
|
|
||||||
if (angle == 0)
|
if (angle == 0)
|
||||||
sf2d_draw_rectangle(x, y, width, height, color);
|
sf2d_draw_rectangle(x, y, width, height, color);
|
||||||
else
|
else
|
||||||
sf2d_draw_rectangle_rotate(x, y, width, height, color, angle);
|
sf2d_draw_rectangle_rotate(x, y, width, height, color, angle);
|
||||||
// Two colors : fill with a gradient.
|
|
||||||
} else {
|
|
||||||
u32 color2 = luaL_checkinteger(L, 7);
|
|
||||||
u8 direction = luaL_checkinteger(L, 8);
|
|
||||||
|
|
||||||
if (angle == 0)
|
|
||||||
sf2d_draw_rectangle_gradient(x, y, width, height, color, color2, direction);
|
|
||||||
else
|
|
||||||
sf2d_draw_rectangle_gradient_rotate(x, y, width, height, color, color2, direction, angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Draw a rectangle outline on the current screen.
|
Draw a circle on the current screen.
|
||||||
@function linedRectangle
|
|
||||||
@tparam integer x rectangle origin horizontal coordinate, in pixels
|
|
||||||
@tparam integer y rectangle origin vertical coordinate, in pixels
|
|
||||||
@tparam integer width rectangle width, in pixels
|
|
||||||
@tparam integer height rectangle height, in pixels
|
|
||||||
@tparam[opt=1] integer lineWidth line's thickness, in pixels
|
|
||||||
@tparam[opt=0] number angle rectangle rotation, in radians
|
|
||||||
@tparam[opt=default color] integer color drawing color
|
|
||||||
*/
|
|
||||||
static int gfx_linedRectangle(lua_State *L) {
|
|
||||||
int x = luaL_checkinteger(L, 1);
|
|
||||||
int y = luaL_checkinteger(L, 2);
|
|
||||||
int width = luaL_checkinteger(L, 3);
|
|
||||||
int height = luaL_checkinteger(L, 4);
|
|
||||||
float lineWidth = luaL_optnumber(L, 5, 1.0f);
|
|
||||||
|
|
||||||
float angle = luaL_optnumber(L, 6, 0);
|
|
||||||
u32 color = luaL_optinteger(L, 7, color_default);
|
|
||||||
|
|
||||||
// Corner coordinates
|
|
||||||
int x2 = x + width, y2 = y;
|
|
||||||
int x3 = x2, y3 = y + height;
|
|
||||||
int x4 = x, y4 = y3;
|
|
||||||
|
|
||||||
// Rotate corners
|
|
||||||
if (angle != 0) {
|
|
||||||
int cx = x + width/2, cy = y + height/2;
|
|
||||||
rotatePoint(x, y, cx, cy, angle, &x, &y );
|
|
||||||
rotatePoint(x2, y2, cx, cy, angle, &x2, &y2);
|
|
||||||
rotatePoint(x3, y3, cx, cy, angle, &x3, &y3);
|
|
||||||
rotatePoint(x4, y4, cx, cy, angle, &x4, &y4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw lines
|
|
||||||
sf2d_draw_line(x, y, x2, y2, lineWidth, color);
|
|
||||||
sf2d_draw_line(x2, y2, x3, y3, lineWidth, color);
|
|
||||||
sf2d_draw_line(x3, y3, x4, y4, lineWidth, color);
|
|
||||||
sf2d_draw_line(x4, y4, x, y, lineWidth, color);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Draw a filled circle on the current screen.
|
|
||||||
@function circle
|
@function circle
|
||||||
@tparam integer x circle center horizontal coordinate, in pixels
|
@tparam integer x circle center horizontal coordinate, in pixels
|
||||||
@tparam integer y circle center vertical coordinate, in pixels
|
@tparam integer y circle center vertical coordinate, in pixels
|
||||||
|
|
@ -395,63 +249,13 @@ static int gfx_circle(lua_State *L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Draw a circle outline on the current screen.
|
|
||||||
@function linedCircle
|
|
||||||
@tparam integer x circle center horizontal coordinate, in pixels
|
|
||||||
@tparam integer y circle center vertical coordinate, in pixels
|
|
||||||
@tparam integer radius circle radius, in pixels
|
|
||||||
@tparam[opt=1] integer width line's thickness, in pixels
|
|
||||||
@tparam[opt=default color] integer color drawing color
|
|
||||||
*/
|
|
||||||
static int gfx_linedCircle(lua_State *L) {
|
|
||||||
int x0 = luaL_checkinteger(L, 1);
|
|
||||||
int y0 = luaL_checkinteger(L, 2);
|
|
||||||
int radius = luaL_checkinteger(L, 3);
|
|
||||||
float width = luaL_optnumber(L, 4, 1.0f);
|
|
||||||
|
|
||||||
u32 color = luaL_optinteger(L, 5, color_default);
|
|
||||||
|
|
||||||
for (int r = ceil(radius - width/2), maxr = ceil(radius + width/2)-1; r <= maxr; r++) {
|
|
||||||
// Implementatin of the Andres circle algorithm.
|
|
||||||
int x = 0;
|
|
||||||
int y = r;
|
|
||||||
int d = r - 1;
|
|
||||||
while (y >= x) {
|
|
||||||
// Best way to draw a lot of points, 10/10
|
|
||||||
sf2d_draw_rectangle(x0 + x , y0 + y, 1, 1, color);
|
|
||||||
sf2d_draw_rectangle(x0 + y , y0 + x, 1, 1, color);
|
|
||||||
sf2d_draw_rectangle(x0 - x , y0 + y, 1, 1, color);
|
|
||||||
sf2d_draw_rectangle(x0 - y , y0 + x, 1, 1, color);
|
|
||||||
sf2d_draw_rectangle(x0 + x , y0 - y, 1, 1, color);
|
|
||||||
sf2d_draw_rectangle(x0 + y , y0 - x, 1, 1, color);
|
|
||||||
sf2d_draw_rectangle(x0 - x , y0 - y, 1, 1, color);
|
|
||||||
sf2d_draw_rectangle(x0 - y , y0 - x, 1, 1, color);
|
|
||||||
|
|
||||||
if (d >= 2*x) {
|
|
||||||
d -= 2*x + 1;
|
|
||||||
x++;
|
|
||||||
} else if (d < 2*(r-y)) {
|
|
||||||
d += 2*y - 1;
|
|
||||||
y--;
|
|
||||||
} else {
|
|
||||||
d += 2*(y - x - 1);
|
|
||||||
y--;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Draw a text on the current screen.
|
Draw a text on the current screen.
|
||||||
@function text
|
@function text
|
||||||
@tparam integer x text drawing origin horizontal coordinate, in pixels
|
@tparam integer x text drawing origin horizontal coordinate, in pixels
|
||||||
@tparam integer y text drawing origin vertical coordinate, in pixels
|
@tparam integer y text drawing origin vertical coordinate, in pixels
|
||||||
@tparam string text the text to draw
|
@tparam string text the text to draw
|
||||||
@tparam[opt=default size] integer size drawing size, in pixels
|
@tparam[opt=9] integer size drawing size, in pixels
|
||||||
@tparam[opt=default color] integer color drawing color
|
@tparam[opt=default color] integer color drawing color
|
||||||
@tparam[opt=default font] font font to use
|
@tparam[opt=default font] font font to use
|
||||||
*/
|
*/
|
||||||
|
|
@ -461,7 +265,7 @@ static int gfx_text(lua_State *L) {
|
||||||
size_t len;
|
size_t len;
|
||||||
const char *text = luaL_checklstring(L, 3, &len);
|
const char *text = luaL_checklstring(L, 3, &len);
|
||||||
|
|
||||||
int size = luaL_optinteger(L, 4, textSize);
|
int size = luaL_optinteger(L, 4, 9);
|
||||||
u32 color = luaL_optinteger(L, 5, color_default);
|
u32 color = luaL_optinteger(L, 5, color_default);
|
||||||
font_userdata *font = luaL_testudata(L, 6, "LFont");
|
font_userdata *font = luaL_testudata(L, 6, "LFont");
|
||||||
if (font == NULL) {
|
if (font == NULL) {
|
||||||
|
|
@ -489,7 +293,7 @@ Warning: No UTF32 support.
|
||||||
@tparam integer y text drawing origin vertical coordinate, in pixels
|
@tparam integer y text drawing origin vertical coordinate, in pixels
|
||||||
@tparam string text the text to draw
|
@tparam string text the text to draw
|
||||||
@tparam integer width width of a line, in pixels
|
@tparam integer width width of a line, in pixels
|
||||||
@tparam[opt=default Size] integer size drawing size, in pixels
|
@tparam[opt=9] integer size drawing size, in pixels
|
||||||
@tparam[opt=default color] integer color drawing color
|
@tparam[opt=default color] integer color drawing color
|
||||||
@tparam[opt=default font] font font to use
|
@tparam[opt=default font] font font to use
|
||||||
*/
|
*/
|
||||||
|
|
@ -500,7 +304,7 @@ static int gfx_wrappedText(lua_State *L) {
|
||||||
const char *text = luaL_checklstring(L, 3, &len);
|
const char *text = luaL_checklstring(L, 3, &len);
|
||||||
unsigned int lineWidth = luaL_checkinteger(L, 4);
|
unsigned int lineWidth = luaL_checkinteger(L, 4);
|
||||||
|
|
||||||
int size = luaL_optinteger(L, 5, textSize);
|
int size = luaL_optinteger(L, 5, 9);
|
||||||
u32 color = luaL_optinteger(L, 6, color_default);
|
u32 color = luaL_optinteger(L, 6, color_default);
|
||||||
font_userdata *font = luaL_testudata(L, 7, "LFont");
|
font_userdata *font = luaL_testudata(L, 7, "LFont");
|
||||||
if (font == NULL) {
|
if (font == NULL) {
|
||||||
|
|
@ -526,7 +330,7 @@ Calculate the size of a text draw with `wrappedText`.
|
||||||
@function calcBoundingBox
|
@function calcBoundingBox
|
||||||
@tparam string text The text to check
|
@tparam string text The text to check
|
||||||
@tparam integer lineWidth width of a line, in pixels
|
@tparam integer lineWidth width of a line, in pixels
|
||||||
@tparam[opt=default size] integer size drawing size, in pixels
|
@tparam[opt=9] integer size drawing size, in pixels
|
||||||
@tparam[opt=default font] font font to use
|
@tparam[opt=default font] font font to use
|
||||||
@treturn integer width of the text, in pixels
|
@treturn integer width of the text, in pixels
|
||||||
@treturn integer height of the text, in pixels
|
@treturn integer height of the text, in pixels
|
||||||
|
|
@ -535,7 +339,7 @@ static int gfx_calcBoundingBox(lua_State *L) {
|
||||||
size_t len;
|
size_t len;
|
||||||
const char *text = luaL_checklstring(L, 1, &len);
|
const char *text = luaL_checklstring(L, 1, &len);
|
||||||
unsigned int lineWidth = luaL_checkinteger(L, 2);
|
unsigned int lineWidth = luaL_checkinteger(L, 2);
|
||||||
int size = luaL_optinteger(L, 3, textSize);
|
int size = luaL_optinteger(L, 3, 9);
|
||||||
font_userdata *font = luaL_testudata(L, 4, "LFont");
|
font_userdata *font = luaL_testudata(L, 4, "LFont");
|
||||||
if (font == NULL) {
|
if (font == NULL) {
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, "LFontDefault");
|
lua_getfield(L, LUA_REGISTRYINDEX, "LFontDefault");
|
||||||
|
|
@ -552,192 +356,6 @@ static int gfx_calcBoundingBox(lua_State *L) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Enables or disable the scissor test.
|
|
||||||
When the scissor test is enabled, the drawing area will be limited to a specific rectangle, every pixel drawn outside will be discarded.
|
|
||||||
Calls this function without argument to disable the scissor test.
|
|
||||||
@function scissor
|
|
||||||
@tparam integer x scissor rectangle origin horizontal coordinate, in pixels
|
|
||||||
@tparam integer y scissor rectangle origin vertical coordinate, in pixels
|
|
||||||
@tparam integer width scissor rectangle width, in pixels
|
|
||||||
@tparam integer height scissor rectangle height, in pixels
|
|
||||||
@tparam[opt=false] boolean invert if true the scissor will be inverted (will draw only outside of the rectangle)
|
|
||||||
*/
|
|
||||||
static int gfx_scissor(lua_State *L) {
|
|
||||||
if (lua_gettop(L) == 0) {
|
|
||||||
lua_scissor.mode = GPU_SCISSOR_DISABLE;
|
|
||||||
lua_scissor.x = 0;
|
|
||||||
lua_scissor.y = 0;
|
|
||||||
lua_scissor.width = 0;
|
|
||||||
lua_scissor.height = 0;
|
|
||||||
} else {
|
|
||||||
lua_scissor.x = luaL_checkinteger(L, 1);
|
|
||||||
lua_scissor.y = luaL_checkinteger(L, 2);
|
|
||||||
lua_scissor.width = luaL_checkinteger(L, 3);
|
|
||||||
lua_scissor.height = luaL_checkinteger(L, 4);
|
|
||||||
lua_scissor.mode = lua_toboolean(L, 5) ? GPU_SCISSOR_INVERT : GPU_SCISSOR_NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sf2d_set_scissor_test(lua_scissor.mode, lua_scissor.x, lua_scissor.y, lua_scissor.width, lua_scissor.height);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
__Work in progress__. Create a render target. Don't use it.
|
|
||||||
@function target
|
|
||||||
@tparam integer width
|
|
||||||
@tparam integer height
|
|
||||||
@treturn target
|
|
||||||
*/
|
|
||||||
static int gfx_target(lua_State *L) {
|
|
||||||
int width = luaL_checkinteger(L, 1);
|
|
||||||
int height = luaL_checkinteger(L, 2);
|
|
||||||
int wpo2 = 0, hpo2 = 0;
|
|
||||||
for (;width>pow(2,wpo2);wpo2++);
|
|
||||||
width = pow(2,wpo2);
|
|
||||||
for (;height>pow(2,hpo2);hpo2++);
|
|
||||||
height = pow(2,hpo2);
|
|
||||||
|
|
||||||
target_userdata *target;
|
|
||||||
target = (target_userdata*)lua_newuserdata(L, sizeof(*target));
|
|
||||||
|
|
||||||
luaL_getmetatable(L, "LTarget");
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
|
|
||||||
target->target = sf2d_create_rendertarget(width, height);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Render targets
|
|
||||||
@section target
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***
|
|
||||||
Clear a target to a specified color.
|
|
||||||
@function :clear
|
|
||||||
@tparam[opt=default color] integer color color to fill the target with
|
|
||||||
*/
|
|
||||||
static int gfx_target_clear(lua_State *L) {
|
|
||||||
target_userdata *target = luaL_checkudata(L, 1, "LTarget");
|
|
||||||
u32 color = luaL_optinteger(L, 2, color_default);
|
|
||||||
|
|
||||||
sf2d_clear_target(target->target, color);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Destroy a target.
|
|
||||||
@function :destroy
|
|
||||||
*/
|
|
||||||
static int gfx_target_destroy(lua_State *L) {
|
|
||||||
target_userdata *target = luaL_checkudata(L, 1, "LTarget");
|
|
||||||
|
|
||||||
sf2d_free_target(target->target);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct luaL_Reg target_methods[];
|
|
||||||
/***
|
|
||||||
|
|
||||||
*/
|
|
||||||
static int gfx_target___index(lua_State *L) {
|
|
||||||
target_userdata *target = luaL_checkudata(L, 1, "LTarget");
|
|
||||||
const char* name = luaL_checkstring(L, 2);
|
|
||||||
|
|
||||||
if (strcmp(name, "texture") == 0) {
|
|
||||||
texture_userdata *texture;
|
|
||||||
texture = (texture_userdata*)lua_newuserdata(L, sizeof(*texture));
|
|
||||||
luaL_getmetatable(L, "LTexture");
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
|
|
||||||
texture->texture = &(target->target->texture);
|
|
||||||
texture->scaleX = 1.0f;
|
|
||||||
texture->scaleY = 1.0f;
|
|
||||||
texture->blendColor = 0xffffffff;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else if (strcmp(name, "duck") == 0) {
|
|
||||||
sf2d_rendertarget *target = sf2d_create_rendertarget(64, 64);
|
|
||||||
for(int i=0;;i++) {
|
|
||||||
sf2d_clear_target(target, 0xff000000);
|
|
||||||
sf2d_start_frame_target(target);
|
|
||||||
sf2d_draw_fill_circle(i%380, i%200, 10, 0xff0000ff);
|
|
||||||
sf2d_end_frame();
|
|
||||||
//sf2d_texture_tile32(&target->texture);
|
|
||||||
|
|
||||||
sf2d_start_frame(GFX_TOP, GFX_LEFT);
|
|
||||||
sf2d_draw_texture(&target->texture, 10, 10);
|
|
||||||
sf2d_end_frame();
|
|
||||||
sf2d_swapbuffers();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (u8 i=0;target_methods[i].name;i++) {
|
|
||||||
if (strcmp(target_methods[i].name, name) == 0) {
|
|
||||||
lua_pushcfunction(L, target_methods[i].func);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Console
|
|
||||||
@section console
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***
|
|
||||||
Initialize the console. You can print on it using print(), or any function that normally outputs to stdout.
|
|
||||||
Warning: you can't use a screen for both a console and drawing, you have to disable the console first.
|
|
||||||
@function console
|
|
||||||
@tparam[opt=gfx.TOP] number screen screen to draw the console on.
|
|
||||||
@tparam[opt=false] boolean debug enable stderr output on the console
|
|
||||||
*/
|
|
||||||
u8 consoleScreen = GFX_TOP;
|
|
||||||
static int gfx_console(lua_State *L) {
|
|
||||||
consoleScreen = luaL_optinteger(L, 1, GFX_TOP);
|
|
||||||
bool err = false;
|
|
||||||
if (lua_isboolean(L, 2)) {
|
|
||||||
err = lua_toboolean(L, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
consoleInit(consoleScreen, NULL);
|
|
||||||
if (err)
|
|
||||||
consoleDebugInit(debugDevice_CONSOLE);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Clear the console.
|
|
||||||
@function clearConsole
|
|
||||||
*/
|
|
||||||
static int gfx_clearConsole(lua_State *L) {
|
|
||||||
consoleClear();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Disable the console.
|
|
||||||
@function disableConsole
|
|
||||||
*/
|
|
||||||
static int gfx_disableConsole(lua_State *L) {
|
|
||||||
gfxSetScreenFormat(consoleScreen, GSP_BGR8_OES);
|
|
||||||
gfxSetDoubleBuffering(consoleScreen, true);
|
|
||||||
gfxSwapBuffersGpu();
|
|
||||||
gspWaitForVBlank();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
static const struct luaL_Reg gfx_lib[] = {
|
static const struct luaL_Reg gfx_lib[] = {
|
||||||
{ "start", gfx_start },
|
{ "start", gfx_start },
|
||||||
|
|
@ -749,38 +367,17 @@ static const struct luaL_Reg gfx_lib[] = {
|
||||||
{ "setVBlankWait", gfx_setVBlankWait },
|
{ "setVBlankWait", gfx_setVBlankWait },
|
||||||
{ "waitForVBlank", gfx_waitForVBlank },
|
{ "waitForVBlank", gfx_waitForVBlank },
|
||||||
{ "vramSpaceFree", gfx_vramSpaceFree },
|
{ "vramSpaceFree", gfx_vramSpaceFree },
|
||||||
{ "point", gfx_point },
|
|
||||||
{ "line", gfx_line },
|
{ "line", gfx_line },
|
||||||
{ "triangle", gfx_triangle },
|
{ "point", gfx_point },
|
||||||
{ "linedTriangle", gfx_linedTriangle },
|
|
||||||
{ "rectangle", gfx_rectangle },
|
{ "rectangle", gfx_rectangle },
|
||||||
{ "linedRectangle", gfx_linedRectangle },
|
|
||||||
{ "circle", gfx_circle },
|
{ "circle", gfx_circle },
|
||||||
{ "linedCircle", gfx_linedCircle },
|
|
||||||
{ "text", gfx_text },
|
{ "text", gfx_text },
|
||||||
{ "wrappedText", gfx_wrappedText },
|
{ "wrappedText", gfx_wrappedText },
|
||||||
{ "calcBoundingBox", gfx_calcBoundingBox },
|
{ "calcBoundingBox", gfx_calcBoundingBox },
|
||||||
{ "scissor", gfx_scissor },
|
|
||||||
{ "target", gfx_target },
|
|
||||||
{ "console", gfx_console },
|
|
||||||
{ "clearConsole", gfx_clearConsole },
|
|
||||||
{ "disableConsole", gfx_disableConsole },
|
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render target
|
// Constants
|
||||||
static const struct luaL_Reg target_methods[] = {
|
|
||||||
{ "__index", gfx_target___index },
|
|
||||||
{ "clear", gfx_target_clear },
|
|
||||||
{ "destroy", gfx_target_destroy },
|
|
||||||
{ "__gc", gfx_target_destroy },
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
/***
|
|
||||||
Constants
|
|
||||||
@section constants
|
|
||||||
*/
|
|
||||||
struct { char *name; int value; } gfx_constants[] = {
|
struct { char *name; int value; } gfx_constants[] = {
|
||||||
/***
|
/***
|
||||||
Constant used to select the top screen.
|
Constant used to select the top screen.
|
||||||
|
|
@ -830,16 +427,6 @@ struct { char *name; int value; } gfx_constants[] = {
|
||||||
@field BOTTOM_WIDTH
|
@field BOTTOM_WIDTH
|
||||||
*/
|
*/
|
||||||
{ "BOTTOM_WIDTH", 320 },
|
{ "BOTTOM_WIDTH", 320 },
|
||||||
/***
|
|
||||||
Represents a vertical gradient drawn from the top (first color) to the bottom (second color).
|
|
||||||
@field TOP_TO_BOTTOM
|
|
||||||
*/
|
|
||||||
{ "TOP_TO_BOTTOM", SF2D_TOP_TO_BOTTOM },
|
|
||||||
/***
|
|
||||||
Represents a horizontal gradient drawn from the left (first color) to the right (second color).
|
|
||||||
@field LEFT_TO_RIGHT
|
|
||||||
*/
|
|
||||||
{ "LEFT_TO_RIGHT", SF2D_LEFT_TO_RIGHT },
|
|
||||||
{ NULL, 0 }
|
{ NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -853,11 +440,6 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); }
|
||||||
};
|
};
|
||||||
|
|
||||||
int luaopen_gfx_lib(lua_State *L) {
|
int luaopen_gfx_lib(lua_State *L) {
|
||||||
luaL_newmetatable(L, "LTarget");
|
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
luaL_setfuncs(L, target_methods, 0);
|
|
||||||
|
|
||||||
luaL_newlib(L, gfx_lib);
|
luaL_newlib(L, gfx_lib);
|
||||||
|
|
||||||
for (int i = 0; gfx_constants[i].name; i++) {
|
for (int i = 0; gfx_constants[i].name; i++) {
|
||||||
|
|
|
||||||
12
source/gfx.h
12
source/gfx.h
|
|
@ -1,12 +0,0 @@
|
||||||
#ifndef GFX_H
|
|
||||||
#define GFX_H
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
GPU_SCISSORMODE mode;
|
|
||||||
u32 x; u32 y;
|
|
||||||
u32 width; u32 height;
|
|
||||||
} scissor_state;
|
|
||||||
|
|
||||||
extern scissor_state lua_scissor;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
23
source/hid.c
23
source/hid.c
|
|
@ -1,12 +1,11 @@
|
||||||
/***
|
/***
|
||||||
The `hid` module.
|
The `hid` module.
|
||||||
The circle pad pro is supported, it's keys replace the "3ds only" keys
|
The circle pad pro is supported, it's keys replace de "3ds only" keys
|
||||||
@module ctr.hid
|
@module ctr.hid
|
||||||
@usage local hid = require("ctr.hid")
|
@usage local hid = require("ctr.hid")
|
||||||
*/
|
*/
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/services/hid.h>
|
#include <3ds/services/hid.h>
|
||||||
#include <3ds/services/irrst.h>
|
|
||||||
|
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
@ -176,25 +175,6 @@ static int hid_circle(lua_State *L) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Return the C-stick position.
|
|
||||||
`0,0` is the center position, and the stick should return to it if not touched (95% of the time).
|
|
||||||
Range is from `-146` to `146` on both X and Y, but these are hard to reach.
|
|
||||||
@newonly
|
|
||||||
@function cstick
|
|
||||||
@treturn number X position
|
|
||||||
@treturn number Y position
|
|
||||||
*/
|
|
||||||
static int hid_cstick(lua_State *L) {
|
|
||||||
circlePosition pos;
|
|
||||||
irrstCstickRead(&pos);
|
|
||||||
|
|
||||||
lua_pushinteger(L, pos.dx);
|
|
||||||
lua_pushinteger(L, pos.dy);
|
|
||||||
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Return the accelerometer vector
|
Return the accelerometer vector
|
||||||
@function accel
|
@function accel
|
||||||
|
|
@ -263,7 +243,6 @@ static const struct luaL_Reg hid_lib[] = {
|
||||||
{ "keys", hid_keys },
|
{ "keys", hid_keys },
|
||||||
{ "touch", hid_touch },
|
{ "touch", hid_touch },
|
||||||
{ "circle", hid_circle },
|
{ "circle", hid_circle },
|
||||||
{ "cstick", hid_cstick },
|
|
||||||
{ "accel", hid_accel },
|
{ "accel", hid_accel },
|
||||||
{ "gyro", hid_gyro },
|
{ "gyro", hid_gyro },
|
||||||
{ "volume", hid_volume },
|
{ "volume", hid_volume },
|
||||||
|
|
|
||||||
153
source/httpc.c
153
source/httpc.c
|
|
@ -9,7 +9,6 @@ The `httpc` module.
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/services/httpc.h>
|
#include <3ds/services/httpc.h>
|
||||||
#include <3ds/services/sslc.h>
|
|
||||||
|
|
||||||
#include <lapi.h>
|
#include <lapi.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
@ -23,6 +22,12 @@ Create a HTTP Context.
|
||||||
*/
|
*/
|
||||||
static int httpc_context(lua_State *L) {
|
static int httpc_context(lua_State *L) {
|
||||||
httpcContext context;
|
httpcContext context;
|
||||||
|
Result ret = httpcOpenContext(&context, "http://google.com/", 0); // Initialization only.
|
||||||
|
if (ret != 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushinteger(L, ret);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
lua_newuserdata(L, sizeof(&context));
|
lua_newuserdata(L, sizeof(&context));
|
||||||
luaL_getmetatable(L, "LHTTPC");
|
luaL_getmetatable(L, "LHTTPC");
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
|
|
@ -39,30 +44,15 @@ context object
|
||||||
Open an url in the context.
|
Open an url in the context.
|
||||||
@function :open
|
@function :open
|
||||||
@tparam string url the url to open
|
@tparam string url the url to open
|
||||||
@tparam[opt="GET"] string method method to use; can be `"GET"`, `"POST"`, `"HEAD"`, `"PUT"` or `"DELETE"`
|
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int httpc_open(lua_State *L) {
|
static int httpc_open(lua_State *L) {
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
httpcContext *context = lua_touserdata(L, 1);
|
||||||
char *url = (char*)luaL_checkstring(L, 2);
|
char *url = (char*)luaL_checkstring(L, 2);
|
||||||
char *smethod = (char*)luaL_optstring(L, 3, "GET");
|
|
||||||
HTTPC_RequestMethod method = HTTPC_METHOD_GET; // default to GET
|
|
||||||
if (strcmp(smethod, "POST")) {
|
|
||||||
method = HTTPC_METHOD_POST;
|
|
||||||
} else if (strcmp(smethod, "HEAD")) {
|
|
||||||
method = HTTPC_METHOD_HEAD;
|
|
||||||
} else if (strcmp(smethod, "PUT")) {
|
|
||||||
method = HTTPC_METHOD_PUT;
|
|
||||||
} else if (strcmp(smethod, "DELETE")) {
|
|
||||||
method = HTTPC_METHOD_DELETE;
|
|
||||||
}
|
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
|
|
||||||
ret = httpcOpenContext(context, method, url, 0);
|
ret = httpcOpenContext(context, url, 0);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
lua_pushboolean(L, false);
|
lua_pushnil(L);
|
||||||
lua_pushinteger(L, ret);
|
lua_pushinteger(L, ret);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
@ -75,9 +65,6 @@ Add a field in the request header.
|
||||||
@function :addRequestHeaderField
|
@function :addRequestHeaderField
|
||||||
@tparam string name Name of the field
|
@tparam string name Name of the field
|
||||||
@tparam string value Value of the field
|
@tparam string value Value of the field
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int httpc_addRequestHeaderField(lua_State *L) {
|
static int httpc_addRequestHeaderField(lua_State *L) {
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
httpcContext *context = lua_touserdata(L, 1);
|
||||||
|
|
@ -86,7 +73,7 @@ static int httpc_addRequestHeaderField(lua_State *L) {
|
||||||
|
|
||||||
Result ret = httpcAddRequestHeaderField(context, name ,value);
|
Result ret = httpcAddRequestHeaderField(context, name ,value);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
lua_pushboolean(L, false);
|
lua_pushnil(L);
|
||||||
lua_pushinteger(L, ret);
|
lua_pushinteger(L, ret);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
@ -97,9 +84,6 @@ static int httpc_addRequestHeaderField(lua_State *L) {
|
||||||
/***
|
/***
|
||||||
Begin a request to get the content at the URL.
|
Begin a request to get the content at the URL.
|
||||||
@function :beginRequest
|
@function :beginRequest
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int httpc_beginRequest(lua_State *L) {
|
static int httpc_beginRequest(lua_State *L) {
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
httpcContext *context = lua_touserdata(L, 1);
|
||||||
|
|
@ -107,7 +91,7 @@ static int httpc_beginRequest(lua_State *L) {
|
||||||
|
|
||||||
ret = httpcBeginRequest(context);
|
ret = httpcBeginRequest(context);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
lua_pushboolean(L, false);
|
lua_pushnil(L);
|
||||||
lua_pushinteger(L, ret);
|
lua_pushinteger(L, ret);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
@ -118,9 +102,7 @@ static int httpc_beginRequest(lua_State *L) {
|
||||||
/***
|
/***
|
||||||
Return the status code returned by the request.
|
Return the status code returned by the request.
|
||||||
@function :getStatusCode
|
@function :getStatusCode
|
||||||
@treturn[1] integer the status code
|
@treturn number the status code
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int httpc_getStatusCode(lua_State *L) {
|
static int httpc_getStatusCode(lua_State *L) {
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
httpcContext *context = lua_touserdata(L, 1);
|
||||||
|
|
@ -154,9 +136,7 @@ static int httpc_getDownloadSize(lua_State *L) {
|
||||||
/***
|
/***
|
||||||
Download and return the data of the context.
|
Download and return the data of the context.
|
||||||
@function :downloadData
|
@function :downloadData
|
||||||
@treturn[1] string data
|
@treturn string data
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int httpc_downloadData(lua_State *L) {
|
static int httpc_downloadData(lua_State *L) {
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
httpcContext *context = lua_touserdata(L, 1);
|
||||||
|
|
@ -174,16 +154,15 @@ static int httpc_downloadData(lua_State *L) {
|
||||||
|
|
||||||
ret = httpcDownloadData(context, buff, size, NULL);
|
ret = httpcDownloadData(context, buff, size, NULL);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
free(buff);
|
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushinteger(L, ret);
|
lua_pushinteger(L, ret);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_pushstring(L, (char*)buff);
|
lua_pushstring(L, (char*)buff);
|
||||||
free(buff);
|
//free(buff);
|
||||||
//lua_pushinteger(L, size); // only for test purposes.
|
lua_pushinteger(L, size); // only for test purposes.
|
||||||
return 1;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
|
@ -198,100 +177,6 @@ static int httpc_close(lua_State *L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Add a POST form field to a HTTP context.
|
|
||||||
@function :addPostData
|
|
||||||
@tparam string name name of the field
|
|
||||||
@tparam string value value of the field
|
|
||||||
*/
|
|
||||||
static int httpc_addPostData(lua_State *L) {
|
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
|
||||||
char *name = (char*)luaL_checkstring(L, 2);
|
|
||||||
char *value = (char*)luaL_checkstring(L, 3);
|
|
||||||
|
|
||||||
httpcAddPostDataAscii(context, name, value);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Get a header field from a response.
|
|
||||||
@function :getResponseHeader
|
|
||||||
@tparam string name name of the header field to get
|
|
||||||
@tparam[opt=2048] number maximum size of the value to get
|
|
||||||
@treturn string field value
|
|
||||||
*/
|
|
||||||
static int httpc_getResponseHeader(lua_State *L) {
|
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
|
||||||
char *name = (char*)luaL_checkstring(L, 2);
|
|
||||||
u32 maxSize = luaL_checkinteger(L, 3);
|
|
||||||
char* value = 0;
|
|
||||||
|
|
||||||
httpcGetResponseHeader(context, name, value, maxSize);
|
|
||||||
|
|
||||||
lua_pushstring(L, value);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Add a trusted RootCA cert to a context.
|
|
||||||
@function :addTrustedRootCA
|
|
||||||
@tparam string DER certificate
|
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
|
||||||
static int httpc_addTrustedRootCA(lua_State *L) {
|
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
|
||||||
u32 certsize;
|
|
||||||
u8* cert = (u8*)luaL_checklstring(L, 2, (size_t*)&certsize);
|
|
||||||
|
|
||||||
Result ret = httpcAddTrustedRootCA(context, cert, certsize);
|
|
||||||
if (ret != 0) {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushinteger(L, ret);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, true);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Set SSL options for a context.
|
|
||||||
@function :setSSLOptions
|
|
||||||
@tparam boolean disableVerify disable server certificate verification if `true`
|
|
||||||
@tparam[opt=false] boolean tlsv10 use TLS v1.0 if `true`
|
|
||||||
*/
|
|
||||||
static int httpc_setSSLOptions(lua_State *L) {
|
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
|
||||||
|
|
||||||
bool disVer = lua_toboolean(L, 2);
|
|
||||||
bool tsl10 = false;
|
|
||||||
if (lua_isboolean(L, 3))
|
|
||||||
tsl10 = lua_toboolean(L, 3);
|
|
||||||
|
|
||||||
httpcSetSSLOpt(context, (disVer?SSLCOPT_DisableVerify:0)|(tsl10?SSLCOPT_TLSv10:0));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Add all the default certificates to the context.
|
|
||||||
@function addDefaultCert
|
|
||||||
*/
|
|
||||||
static int httpc_addDefaultCert(lua_State *L) {
|
|
||||||
httpcContext *context = lua_touserdata(L, 1);
|
|
||||||
|
|
||||||
httpcAddDefaultCert(context, SSLC_DefaultRootCert_CyberTrust);
|
|
||||||
httpcAddDefaultCert(context, SSLC_DefaultRootCert_AddTrust_External_CA);
|
|
||||||
httpcAddDefaultCert(context, SSLC_DefaultRootCert_COMODO);
|
|
||||||
httpcAddDefaultCert(context, SSLC_DefaultRootCert_USERTrust);
|
|
||||||
httpcAddDefaultCert(context, SSLC_DefaultRootCert_DigiCert_EV);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// object
|
// object
|
||||||
static const struct luaL_Reg httpc_methods[] = {
|
static const struct luaL_Reg httpc_methods[] = {
|
||||||
{"open", httpc_open },
|
{"open", httpc_open },
|
||||||
|
|
@ -301,12 +186,6 @@ static const struct luaL_Reg httpc_methods[] = {
|
||||||
{"getDownloadSize", httpc_getDownloadSize },
|
{"getDownloadSize", httpc_getDownloadSize },
|
||||||
{"downloadData", httpc_downloadData },
|
{"downloadData", httpc_downloadData },
|
||||||
{"close", httpc_close },
|
{"close", httpc_close },
|
||||||
{"__gc", httpc_close },
|
|
||||||
{"addPostData", httpc_addPostData },
|
|
||||||
{"getResponseHeader", httpc_getResponseHeader },
|
|
||||||
{"addTrustedRootCA", httpc_addTrustedRootCA },
|
|
||||||
{"setSSLOptions", httpc_setSSLOptions },
|
|
||||||
{"addDefaultCert", httpc_addDefaultCert },
|
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -329,7 +208,7 @@ int luaopen_httpc_lib(lua_State *L) {
|
||||||
|
|
||||||
void load_httpc_lib(lua_State *L) {
|
void load_httpc_lib(lua_State *L) {
|
||||||
if (!isHttpcInitialized) {
|
if (!isHttpcInitialized) {
|
||||||
httpcInit(0x1000);
|
httpcInit();
|
||||||
isHttpcInitialized = true;
|
isHttpcInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
30
source/ir.c
30
source/ir.c
|
|
@ -5,7 +5,7 @@ The `ir` module.
|
||||||
*/
|
*/
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/services/ir.h>
|
#include <3ds/services/ir.h>
|
||||||
//#include <3ds/linear.h>
|
#include <3ds/linear.h>
|
||||||
|
|
||||||
#include <lualib.h>
|
#include <lualib.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
@ -37,9 +37,6 @@ Bitrate codes list (this is not a part of the module, just a reference)
|
||||||
Initialize the IR module.
|
Initialize the IR module.
|
||||||
@function init
|
@function init
|
||||||
@tparam[opt=6] number bitrate bitrate of the IR module (more informations below)
|
@tparam[opt=6] number bitrate bitrate of the IR module (more informations below)
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int ir_init(lua_State *L) {
|
static int ir_init(lua_State *L) {
|
||||||
u8 bitrate = luaL_optinteger(L, 1, 6);
|
u8 bitrate = luaL_optinteger(L, 1, 6);
|
||||||
|
|
@ -59,9 +56,6 @@ static int ir_init(lua_State *L) {
|
||||||
/***
|
/***
|
||||||
Disable the IR module.
|
Disable the IR module.
|
||||||
@function shutdown
|
@function shutdown
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int ir_shutdown(lua_State *L) {
|
static int ir_shutdown(lua_State *L) {
|
||||||
Result ret = IRU_Shutdown();
|
Result ret = IRU_Shutdown();
|
||||||
|
|
@ -80,9 +74,6 @@ Send some data over the IR module.
|
||||||
@function send
|
@function send
|
||||||
@tparam string data just some data
|
@tparam string data just some data
|
||||||
@tparam[opt=false] boolean wait set to `true` to wait until the data is sent.
|
@tparam[opt=false] boolean wait set to `true` to wait until the data is sent.
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int ir_send(lua_State *L) {
|
static int ir_send(lua_State *L) {
|
||||||
u8 *data = (u8*)luaL_checkstring(L, 1);
|
u8 *data = (u8*)luaL_checkstring(L, 1);
|
||||||
|
|
@ -107,9 +98,7 @@ Receive some data from the IR module.
|
||||||
@function receive
|
@function receive
|
||||||
@tparam number size bytes to receive
|
@tparam number size bytes to receive
|
||||||
@tparam[opt=false] boolean wait wait until the data is received
|
@tparam[opt=false] boolean wait wait until the data is received
|
||||||
@treturn[1] string data
|
@return string data
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int ir_receive(lua_State *L) {
|
static int ir_receive(lua_State *L) {
|
||||||
u32 size = luaL_checkinteger(L, 1);
|
u32 size = luaL_checkinteger(L, 1);
|
||||||
|
|
@ -119,7 +108,7 @@ static int ir_receive(lua_State *L) {
|
||||||
|
|
||||||
Result ret = iruRecvData(data, size, 0x00, &transfercount, wait);
|
Result ret = iruRecvData(data, size, 0x00, &transfercount, wait);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
lua_pushnil(L);
|
lua_pushboolean(L, false);
|
||||||
lua_pushinteger(L, ret);
|
lua_pushinteger(L, ret);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
@ -133,9 +122,6 @@ static int ir_receive(lua_State *L) {
|
||||||
Set the bitrate of the communication.
|
Set the bitrate of the communication.
|
||||||
@function setBitRate
|
@function setBitRate
|
||||||
@tparam number bitrate new bitrate for the communication
|
@tparam number bitrate new bitrate for the communication
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int ir_setBitRate(lua_State *L) {
|
static int ir_setBitRate(lua_State *L) {
|
||||||
u8 bitrate = luaL_checkinteger(L, 1);
|
u8 bitrate = luaL_checkinteger(L, 1);
|
||||||
|
|
@ -147,24 +133,20 @@ static int ir_setBitRate(lua_State *L) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_pushboolean(L, true);
|
return 0;
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Return the actual bitrate of the communication.
|
Return the actual bitrate of the communication.
|
||||||
@function getBitRate
|
@function getBitRate
|
||||||
@treturn[1] number actual bitrate
|
@treturn number actual bitrate
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int ir_getBitRate(lua_State *L) {
|
static int ir_getBitRate(lua_State *L) {
|
||||||
u8 bitrate = 0;
|
u8 bitrate = 0;
|
||||||
|
|
||||||
Result ret = IRU_GetBitRate(&bitrate);
|
Result ret = IRU_GetBitRate(&bitrate);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
lua_pushnil(L);
|
lua_pushboolean(L, false);
|
||||||
lua_pushinteger(L, ret);
|
lua_pushinteger(L, ret);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
|
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
|
|
@ -34,14 +32,7 @@ void error(const char *error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
int main(int argc, char** argv) {
|
int main() {
|
||||||
// Default arguments
|
|
||||||
#ifdef ROMFS
|
|
||||||
char* mainFile = "romfs:/main.lua";
|
|
||||||
#else
|
|
||||||
char* mainFile = "main.lua";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Init Lua
|
// Init Lua
|
||||||
lua_State *L = luaL_newstate();
|
lua_State *L = luaL_newstate();
|
||||||
if (L == NULL) {
|
if (L == NULL) {
|
||||||
|
|
@ -52,32 +43,9 @@ int main(int argc, char** argv) {
|
||||||
// Load libs
|
// Load libs
|
||||||
luaL_openlibs(L);
|
luaL_openlibs(L);
|
||||||
load_ctr_lib(L);
|
load_ctr_lib(L);
|
||||||
isGfxInitialized = true;
|
|
||||||
|
|
||||||
// Parse arguments
|
|
||||||
for (int i=0;i<argc;i++) {
|
|
||||||
if (argv[i][0] == '-') {
|
|
||||||
switch(argv[i][1]) {
|
|
||||||
case 'm': { // main file replacement
|
|
||||||
mainFile = &argv[i][2];
|
|
||||||
if (argv[i][2] == ' ') mainFile = &argv[i][3];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'r': { // root directory replacement
|
|
||||||
char* root;
|
|
||||||
root = &argv[i][2];
|
|
||||||
if (argv[i][2] == ' ') root = &argv[i][3];
|
|
||||||
if (chdir(root)) error("No such root path");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the actual thing
|
// Do the actual thing
|
||||||
if (luaL_dofile(L, mainFile)) error(luaL_checkstring(L, -1));
|
if (luaL_dofile(L, "main.lua")) error(luaL_checkstring(L, -1));
|
||||||
|
|
||||||
// Unload libs
|
// Unload libs
|
||||||
unload_ctr_lib(L);
|
unload_ctr_lib(L);
|
||||||
|
|
|
||||||
73
source/map.c
73
source/map.c
|
|
@ -1,5 +1,5 @@
|
||||||
/***
|
/***
|
||||||
The `gfx.map` module.
|
The `map` module.
|
||||||
Tile coordinates start at x=0,y=0.
|
Tile coordinates start at x=0,y=0.
|
||||||
@module ctr.gfx.map
|
@module ctr.gfx.map
|
||||||
@usage local map = require("ctr.gfx.map")
|
@usage local map = require("ctr.gfx.map")
|
||||||
|
|
@ -13,9 +13,7 @@ Tile coordinates start at x=0,y=0.
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "gfx.h"
|
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -49,9 +47,7 @@ Load a map from a file.
|
||||||
@tparam texture tileset containing the tileset
|
@tparam texture tileset containing the tileset
|
||||||
@tparam number tileWidth tile width
|
@tparam number tileWidth tile width
|
||||||
@tparam number tileHeight tile height
|
@tparam number tileHeight tile height
|
||||||
@treturn[1] map loaded map object
|
@treturn map loaded map object
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
*/
|
||||||
static int map_load(lua_State *L) {
|
static int map_load(lua_State *L) {
|
||||||
texture_userdata *texture = luaL_checkudata(L, 2, "LTexture");
|
texture_userdata *texture = luaL_checkudata(L, 2, "LTexture");
|
||||||
|
|
@ -62,15 +58,6 @@ static int map_load(lua_State *L) {
|
||||||
luaL_getmetatable(L, "LMap");
|
luaL_getmetatable(L, "LMap");
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
// Block GC of the texture by keeping a reference to it in the registry
|
|
||||||
// registry[map_userdata] = texture_userdata
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_copy(L, -2, -1); // map_userdata
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_copy(L, 2, -1); // texture_userdata
|
|
||||||
lua_settable(L, LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
// Init userdata fields
|
|
||||||
map->texture = texture;
|
map->texture = texture;
|
||||||
map->tileSizeX = tileSizeX;
|
map->tileSizeX = tileSizeX;
|
||||||
map->tileSizeY = tileSizeY;
|
map->tileSizeY = tileSizeY;
|
||||||
|
|
@ -169,81 +156,50 @@ Map object
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Draw (a part of) the map on the screen.
|
Draw a map.
|
||||||
@function :draw
|
@function :draw
|
||||||
@tparam integer x X top-left coordinate to draw the map on the screen (pixels)
|
@tparam number x X position
|
||||||
@tparam integer y Y top-left coordinate to draw the map on the screen (pixels)
|
@tparam number y Y position
|
||||||
@tparam[opt=0] integer offsetX drawn area X start coordinate on the map (pixels) (x=0,y=0 correspond to the first tile top-left corner)
|
@within Methods
|
||||||
@tparam[opt=0] integer offsetY drawn area Y start coordinate on the map (pixels)
|
|
||||||
@tparam[opt=400] integer width width of the drawn area on the map (pixels)
|
|
||||||
@tparam[opt=240] integer height height of the drawn area on the map (pixels)
|
|
||||||
@usage
|
|
||||||
-- This will draw on the screen at x=5,y=5 a part of the map. The part is the rectangle on the map starting at x=16,y=16 and width=32,height=48.
|
|
||||||
-- For example, if you use 16x16 pixel tiles, this will draw the tiles from 1,1 (top-left corner of the rectangle) to 2,3 (bottom-right corner).
|
|
||||||
map:draw(5, 5, 16, 16, 32, 48)
|
|
||||||
*/
|
*/
|
||||||
static int map_draw(lua_State *L) {
|
static int map_draw(lua_State *L) {
|
||||||
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
||||||
int x = luaL_checkinteger(L, 2);
|
int x = luaL_checkinteger(L, 2);
|
||||||
int y = luaL_checkinteger(L, 3);
|
int y = luaL_checkinteger(L, 3);
|
||||||
int offsetX = luaL_optinteger(L, 4, 0);
|
|
||||||
int offsetY = luaL_optinteger(L, 5, 0);
|
|
||||||
int width = luaL_optinteger(L, 6, 400);
|
|
||||||
int height = luaL_optinteger(L, 7, 240);
|
|
||||||
|
|
||||||
int xI = fmax(floor((double)offsetX / map->tileSizeX), 0); // initial tile X
|
|
||||||
int xF = fmin(ceil((double)(offsetX + width) / map->tileSizeX), map->width); // final tile X
|
|
||||||
|
|
||||||
int yI = fmax(floor((double)offsetY / map->tileSizeY), 0); // initial tile Y
|
|
||||||
int yF = fmin(ceil((double)(offsetY + height) / map->tileSizeY), map->height); // final tile Y
|
|
||||||
|
|
||||||
if (sf2d_get_current_screen() == GFX_TOP)
|
|
||||||
sf2d_set_scissor_test(GPU_SCISSOR_NORMAL, x, y, fmin(width, 400 - x), fmin(height, 240 - y)); // Scissor test doesn't work when x/y + width > screenWidth/Height
|
|
||||||
else
|
|
||||||
sf2d_set_scissor_test(GPU_SCISSOR_NORMAL, x, y, fmin(width, 320 - x), fmin(height, 240 - y));
|
|
||||||
|
|
||||||
int texX = 0;
|
int texX = 0;
|
||||||
int texY = 0;
|
int texY = 0;
|
||||||
|
|
||||||
if (map->texture->blendColor == 0xffffffff) {
|
if (map->texture->blendColor == 0xffffffff) {
|
||||||
for (int xp = xI; xp < xF; xp++) {
|
for (int xp=0; xp<map->width; xp++) {
|
||||||
for (int yp = yI; yp < yF; yp++) {
|
for (int yp=0; yp<map->height; yp++) {
|
||||||
u16 tile = getTile(map, xp, yp);
|
u16 tile = getTile(map, xp, yp);
|
||||||
getTilePos(map, tile, &texX, &texY);
|
getTilePos(map, tile, &texX, &texY);
|
||||||
sf2d_draw_texture_part(map->texture->texture, x+(map->tileSizeX*xp)+(xp*map->spaceX)-offsetX, y+(map->tileSizeY*yp)+(yp*map->spaceY)-offsetY, texX, texY, map->tileSizeX, map->tileSizeY);
|
sf2d_draw_texture_part(map->texture->texture, (x+(map->tileSizeX*xp)+(xp*map->spaceX)), (y+(map->tileSizeY*yp)+(yp*map->spaceY)), texX, texY, map->tileSizeX, map->tileSizeY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int xp = xI; xp < xF; xp++) {
|
for (int xp=0; xp<map->width; xp++) {
|
||||||
for (int yp = yI; yp < yF; yp++) {
|
for (int yp=0; yp<map->height; yp++) {
|
||||||
u16 tile = getTile(map, xp, yp);
|
u16 tile = getTile(map, xp, yp);
|
||||||
getTilePos(map, tile, &texX, &texY);
|
getTilePos(map, tile, &texX, &texY);
|
||||||
sf2d_draw_texture_part_blend(map->texture->texture, x+(map->tileSizeX*xp)+(xp*map->spaceX)-offsetX, y+(map->tileSizeY*yp)+(yp*map->spaceY)-offsetY, texX, texY, map->tileSizeX, map->tileSizeY, map->texture->blendColor);
|
sf2d_draw_texture_part_blend(map->texture->texture, (x+(map->tileSizeX*xp)+(xp*map->spaceX)), (y+(map->tileSizeY*yp)+(yp*map->spaceY)), texX, texY, map->tileSizeX, map->tileSizeY, map->texture->blendColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sf2d_set_scissor_test(lua_scissor.mode, lua_scissor.x, lua_scissor.y, lua_scissor.width, lua_scissor.height);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Unload a map.
|
Unload a map.
|
||||||
@function :unload
|
@function :unload
|
||||||
|
@within Methods
|
||||||
*/
|
*/
|
||||||
static int map_unload(lua_State *L) {
|
static int map_unload(lua_State *L) {
|
||||||
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
||||||
|
|
||||||
free(map->data);
|
free(map->data);
|
||||||
|
|
||||||
// Remove the reference to the texture in the registry
|
|
||||||
// registry[map_userdata] = nil
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_copy(L, 1, -1); // map_userdata
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_settable(L, LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,6 +208,7 @@ Return the size of a map.
|
||||||
@function :getSize
|
@function :getSize
|
||||||
@treturn number width of the map, in tiles
|
@treturn number width of the map, in tiles
|
||||||
@treturn number height of the map, in tiles
|
@treturn number height of the map, in tiles
|
||||||
|
@within Methods
|
||||||
*/
|
*/
|
||||||
static int map_getSize(lua_State *L) {
|
static int map_getSize(lua_State *L) {
|
||||||
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
||||||
|
|
@ -268,6 +225,7 @@ Return the value of a tile.
|
||||||
@tparam number x X position of the tile (in tiles)
|
@tparam number x X position of the tile (in tiles)
|
||||||
@tparam number y Y position of the tile (in tiles)
|
@tparam number y Y position of the tile (in tiles)
|
||||||
@treturn number value of the tile
|
@treturn number value of the tile
|
||||||
|
@within Methods
|
||||||
*/
|
*/
|
||||||
static int map_getTile(lua_State *L) {
|
static int map_getTile(lua_State *L) {
|
||||||
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
||||||
|
|
@ -285,6 +243,7 @@ Set the value of a tile.
|
||||||
@tparam number x X position of the tile (in tiles)
|
@tparam number x X position of the tile (in tiles)
|
||||||
@tparam number y Y position of the tile (in tiles)
|
@tparam number y Y position of the tile (in tiles)
|
||||||
@tparam number value new value for the tile
|
@tparam number value new value for the tile
|
||||||
|
@within Methods
|
||||||
*/
|
*/
|
||||||
static int map_setTile(lua_State *L) {
|
static int map_setTile(lua_State *L) {
|
||||||
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
map_userdata *map = luaL_checkudata(L, 1, "LMap");
|
||||||
|
|
|
||||||
|
|
@ -20,23 +20,20 @@ u32 bufferSize = 0;
|
||||||
Initialize the mic module.
|
Initialize the mic module.
|
||||||
@function init
|
@function init
|
||||||
@tparam[opt=0x50000] number bufferSize size of the buffer (must be a multiple of 0x1000)
|
@tparam[opt=0x50000] number bufferSize size of the buffer (must be a multiple of 0x1000)
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer/string error code/message
|
|
||||||
*/
|
*/
|
||||||
static int mic_init(lua_State *L) {
|
static int mic_init(lua_State *L) {
|
||||||
bufferSize = luaL_optinteger(L, 1, 0x50000);
|
bufferSize = luaL_optinteger(L, 1, 0x50000);
|
||||||
|
|
||||||
buff = memalign(0x1000, bufferSize);
|
buff = memalign(0x1000, bufferSize);
|
||||||
if (buff == NULL) {
|
if (buff == NULL) {
|
||||||
lua_pushboolean(L, false);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "Couldn't allocate buffer");
|
lua_pushstring(L, "Couldn't allocate buffer");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
Result ret = micInit(buff, bufferSize);
|
Result ret = micInit(buff, bufferSize);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
free(buff);
|
free(buff);
|
||||||
lua_pushboolean(L, false);
|
lua_pushnil(L);
|
||||||
lua_pushinteger(L, ret);
|
lua_pushinteger(L, ret);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,13 @@ The `news` module.
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
bool initStateNews = false;
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Initialize the news module.
|
Initialize the news module.
|
||||||
@function init
|
@function init
|
||||||
*/
|
*/
|
||||||
static int news_init(lua_State *L) {
|
static int news_init(lua_State *L) {
|
||||||
if (!initStateNews) {
|
|
||||||
newsInit();
|
newsInit();
|
||||||
initStateNews = true;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,10 +63,8 @@ Disable the news module.
|
||||||
@function shutdown
|
@function shutdown
|
||||||
*/
|
*/
|
||||||
static int news_shutdown(lua_State *L) {
|
static int news_shutdown(lua_State *L) {
|
||||||
if (initStateNews) {
|
|
||||||
newsExit();
|
newsExit();
|
||||||
initStateNews = false;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,10 +83,3 @@ int luaopen_news_lib(lua_State *L) {
|
||||||
void load_news_lib(lua_State *L) {
|
void load_news_lib(lua_State *L) {
|
||||||
luaL_requiref(L, "ctr.news", luaopen_news_lib, 0);
|
luaL_requiref(L, "ctr.news", luaopen_news_lib, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unload_news_lib(lua_State *L) {
|
|
||||||
if (initStateNews) {
|
|
||||||
newsExit();
|
|
||||||
initStateNews = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
25
source/ptm.c
25
source/ptm.c
|
|
@ -10,20 +10,14 @@ The `ptm` module.
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
||||||
bool initStatePTM = false;
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Initialize the PTM module.
|
Initialize the PTM module.
|
||||||
@function init
|
@function init
|
||||||
*/
|
*/
|
||||||
static int ptm_init(lua_State *L) {
|
static int ptm_init(lua_State *L) {
|
||||||
if (!initStatePTM) {
|
|
||||||
ptmuInit();
|
ptmuInit();
|
||||||
ptmSysmInit();
|
ptmSysmInit();
|
||||||
|
|
||||||
initStatePTM = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,26 +26,22 @@ Disable the PTM module.
|
||||||
@function shutdown
|
@function shutdown
|
||||||
*/
|
*/
|
||||||
static int ptm_shutdown(lua_State *L) {
|
static int ptm_shutdown(lua_State *L) {
|
||||||
if (initStatePTM) {
|
|
||||||
ptmuExit();
|
ptmuExit();
|
||||||
ptmSysmExit();
|
ptmSysmExit();
|
||||||
|
|
||||||
initStatePTM = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Return the shell state.
|
Return the shell state.
|
||||||
@function getShellState
|
@function getShellState
|
||||||
@treturn boolean shell state, `true` if open, `false` if closed.
|
@treturn number shell state
|
||||||
*/
|
*/
|
||||||
static int ptm_getShellState(lua_State *L) {
|
static int ptm_getShellState(lua_State *L) {
|
||||||
u8 out = 0;
|
u8 out = 0;
|
||||||
PTMU_GetShellState(&out);
|
PTMU_GetShellState(&out);
|
||||||
|
|
||||||
lua_pushboolean(L, out);
|
lua_pushinteger(L, out);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -117,9 +107,7 @@ Setup the new 3DS CPU features (overclock, 4 cores ...)
|
||||||
@newonly
|
@newonly
|
||||||
@function configureNew3DSCPU
|
@function configureNew3DSCPU
|
||||||
@tparam boolean enable enable the New3DS CPU features
|
@tparam boolean enable enable the New3DS CPU features
|
||||||
@treturn[1] boolean `true` if everything went fine
|
@treturn boolean `true` if everything went fine
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int ptm_configureNew3DSCPU(lua_State *L) {
|
static int ptm_configureNew3DSCPU(lua_State *L) {
|
||||||
u8 conf = false;
|
u8 conf = false;
|
||||||
|
|
@ -158,10 +146,3 @@ int luaopen_ptm_lib(lua_State *L) {
|
||||||
void load_ptm_lib(lua_State *L) {
|
void load_ptm_lib(lua_State *L) {
|
||||||
luaL_requiref(L, "ctr.ptm", luaopen_ptm_lib, 0);
|
luaL_requiref(L, "ctr.ptm", luaopen_ptm_lib, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unload_ptm_lib(lua_State *L) {
|
|
||||||
if (initStatePTM) {
|
|
||||||
ptmuExit();
|
|
||||||
ptmSysmExit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
13
source/qtm.c
13
source/qtm.c
|
|
@ -22,9 +22,6 @@ static const struct luaL_Reg qtm_methods[];
|
||||||
/***
|
/***
|
||||||
Initialize the QTM module.
|
Initialize the QTM module.
|
||||||
@function init
|
@function init
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int qtm_init(lua_State *L) {
|
static int qtm_init(lua_State *L) {
|
||||||
Result ret = qtmInit();
|
Result ret = qtmInit();
|
||||||
|
|
@ -62,7 +59,7 @@ static int qtm_checkInitialized(lua_State *L) {
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Return informations about the headtracking
|
Return informations about the headtracking
|
||||||
@function getHeadtrackingInfo
|
@function getHeadTrackingInfo
|
||||||
@treturn qtmInfos QTM informations
|
@treturn qtmInfos QTM informations
|
||||||
*/
|
*/
|
||||||
static int qtm_getHeadtrackingInfo(lua_State *L) {
|
static int qtm_getHeadtrackingInfo(lua_State *L) {
|
||||||
|
|
@ -124,10 +121,8 @@ Convert QTM coordinates to screen coordinates
|
||||||
@tparam number coordinates index
|
@tparam number coordinates index
|
||||||
@tparam[opt=400] number screenWidth specify a screen width
|
@tparam[opt=400] number screenWidth specify a screen width
|
||||||
@tparam[opt=320] number screenHeight specify a screen height
|
@tparam[opt=320] number screenHeight specify a screen height
|
||||||
@treturn[1] number screen X coordinate
|
@treturn number screen X coordinate
|
||||||
@treturn[1] number screen Y coordinate
|
@treturn number screen Y coordinate
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
*/
|
||||||
static int qtm_convertCoordToScreen(lua_State *L) {
|
static int qtm_convertCoordToScreen(lua_State *L) {
|
||||||
qtm_userdata *info = luaL_checkudata(L, 1, "LQTM");
|
qtm_userdata *info = luaL_checkudata(L, 1, "LQTM");
|
||||||
|
|
@ -135,7 +130,7 @@ static int qtm_convertCoordToScreen(lua_State *L) {
|
||||||
index = index - 1; // Lua index begins at 1
|
index = index - 1; // Lua index begins at 1
|
||||||
if (index > 3 || index < 0) {
|
if (index > 3 || index < 0) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "Index must be between 1 and 3");
|
lua_pushnil(L);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
float screenWidth = luaL_optnumber(L, 3, 400.0f);
|
float screenWidth = luaL_optnumber(L, 3, 400.0f);
|
||||||
|
|
|
||||||
318
source/socket.c
318
source/socket.c
|
|
@ -1,7 +1,6 @@
|
||||||
/***
|
/***
|
||||||
The `socket` module. Almost like luasocket, but for the TCP part only.
|
The `socket` module. Almost like luasocket, but for the TCP part only.
|
||||||
The UDP part is only without connection.
|
The UDP part is only without connection.
|
||||||
All sockets are not blocking by default.
|
|
||||||
@module ctr.socket
|
@module ctr.socket
|
||||||
@usage local socket = require("ctr.socket")
|
@usage local socket = require("ctr.socket")
|
||||||
*/
|
*/
|
||||||
|
|
@ -9,7 +8,6 @@ All sockets are not blocking by default.
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include <3ds/services/soc.h>
|
#include <3ds/services/soc.h>
|
||||||
#include <3ds/services/sslc.h>
|
|
||||||
|
|
||||||
#include <lapi.h>
|
#include <lapi.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
@ -17,9 +15,7 @@ All sockets are not blocking by default.
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
@ -30,78 +26,33 @@ typedef struct {
|
||||||
int socket;
|
int socket;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
struct hostent *host; // only used for client sockets
|
struct hostent *host; // only used for client sockets
|
||||||
sslcContext sslContext;
|
|
||||||
bool isSSL;
|
|
||||||
} socket_userdata;
|
} socket_userdata;
|
||||||
|
|
||||||
bool initStateSocket = false;
|
|
||||||
|
|
||||||
u32 rootCertChain = 0;
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Initialize the socket module
|
Initialize the socket module
|
||||||
@function init
|
@function init
|
||||||
@tparam[opt=0x100000] number buffer size (in bytes), must be a multiple of 0x1000
|
@tparam[opt=0x100000] number buffer size (in bytes), must be a multiple of 0x1000
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] number/string error code/message
|
|
||||||
*/
|
*/
|
||||||
static int socket_init(lua_State *L) {
|
static int socket_init(lua_State *L) {
|
||||||
if (!initStateSocket) {
|
|
||||||
u32 size = luaL_optinteger(L, 1, 0x100000);
|
u32 size = luaL_optinteger(L, 1, 0x100000);
|
||||||
if (size%0x1000 != 0) {
|
Result ret = socInit((u32*)memalign(0x1000, size), size);
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushstring(L, "Not a multiple of 0x1000");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32* mem = (u32*)memalign(0x1000, size);
|
if (ret) {
|
||||||
if (mem == NULL) {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushstring(L, "Failed to allocate memory");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ret = socInit(mem, size);
|
|
||||||
|
|
||||||
if (R_FAILED(ret)) {
|
|
||||||
lua_pushboolean(L, false);
|
lua_pushboolean(L, false);
|
||||||
lua_pushinteger(L, ret);
|
lua_pushinteger(L, ret);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sslcInit(0);
|
|
||||||
if (R_FAILED(ret)) {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushinteger(L, ret);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
sslcCreateRootCertChain(&rootCertChain);
|
|
||||||
sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_CyberTrust, NULL);
|
|
||||||
sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_AddTrust_External_CA, NULL);
|
|
||||||
sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_COMODO, NULL);
|
|
||||||
sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_USERTrust, NULL);
|
|
||||||
sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_DigiCert_EV, NULL);
|
|
||||||
|
|
||||||
initStateSocket = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, true);
|
lua_pushboolean(L, true);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Disable the socket module.
|
Disable the socket module. Must be called before exiting ctrµLua.
|
||||||
@function shutdown
|
@function shutdown
|
||||||
*/
|
*/
|
||||||
static int socket_shutdown(lua_State *L) {
|
static int socket_shutdown(lua_State *L) {
|
||||||
if (initStateSocket) {
|
|
||||||
sslcDestroyRootCertChain(rootCertChain);
|
|
||||||
sslcExit();
|
|
||||||
socExit();
|
socExit();
|
||||||
initStateSocket = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -109,9 +60,7 @@ static int socket_shutdown(lua_State *L) {
|
||||||
/***
|
/***
|
||||||
Return a TCP socket.
|
Return a TCP socket.
|
||||||
@function tcp
|
@function tcp
|
||||||
@treturn[1] TCPMaster TCP socket
|
@treturn TCPMaster TCP socket
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
*/
|
||||||
static int socket_tcp(lua_State *L) {
|
static int socket_tcp(lua_State *L) {
|
||||||
socket_userdata *userdata = lua_newuserdata(L, sizeof(*userdata));
|
socket_userdata *userdata = lua_newuserdata(L, sizeof(*userdata));
|
||||||
|
|
@ -127,18 +76,13 @@ static int socket_tcp(lua_State *L) {
|
||||||
|
|
||||||
userdata->addr.sin_family = AF_INET;
|
userdata->addr.sin_family = AF_INET;
|
||||||
|
|
||||||
userdata->isSSL = false;
|
|
||||||
fcntl(userdata->socket, F_SETFL, fcntl(userdata->socket, F_GETFL, 0)|O_NONBLOCK);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Return an UDP socket.
|
Return an UDP socket.
|
||||||
@function udp
|
@function udp
|
||||||
@treturn[1] UDPMaster UDP socket
|
@treturn UDPMaster UDP socket
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
*/
|
||||||
static int socket_udp(lua_State *L) {
|
static int socket_udp(lua_State *L) {
|
||||||
socket_userdata *userdata = lua_newuserdata(L, sizeof(*userdata));
|
socket_userdata *userdata = lua_newuserdata(L, sizeof(*userdata));
|
||||||
|
|
@ -148,39 +92,15 @@ static int socket_udp(lua_State *L) {
|
||||||
userdata->socket = socket(AF_INET, SOCK_DGRAM, 0);
|
userdata->socket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
if (userdata->socket < 0) {
|
if (userdata->socket < 0) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, strerror(errno));
|
lua_pushstring(L, "Failed to create a TCP socket");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
userdata->addr.sin_family = AF_INET;
|
userdata->addr.sin_family = AF_INET;
|
||||||
fcntl(userdata->socket, F_SETFL, fcntl(userdata->socket, F_GETFL, 0)|O_NONBLOCK);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Add a trusted root CA to the certChain.
|
|
||||||
@function addTrustedRootCA
|
|
||||||
@tparam string cert DER cert
|
|
||||||
@treturn[1] boolean `true` if everything went fine
|
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] number error code
|
|
||||||
*/
|
|
||||||
static int socket_addTrustedRootCA(lua_State *L) {
|
|
||||||
size_t size = 0;
|
|
||||||
const char* cert = luaL_checklstring(L, 1, &size);
|
|
||||||
|
|
||||||
Result ret = sslcAddTrustedRootCA(rootCertChain, (u8*)cert, size, NULL);
|
|
||||||
if (R_FAILED(ret)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushinteger(L, ret);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, true);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
All sockets
|
All sockets
|
||||||
@section sockets
|
@section sockets
|
||||||
|
|
@ -195,7 +115,7 @@ static int socket_bind(lua_State *L) {
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
||||||
int port = luaL_checkinteger(L, 2);
|
int port = luaL_checkinteger(L, 2);
|
||||||
|
|
||||||
userdata->addr.sin_addr.s_addr = gethostid();
|
userdata->addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
userdata->addr.sin_port = htons(port);
|
userdata->addr.sin_port = htons(port);
|
||||||
|
|
||||||
bind(userdata->socket, (struct sockaddr*)&userdata->addr, sizeof(userdata->addr));
|
bind(userdata->socket, (struct sockaddr*)&userdata->addr, sizeof(userdata->addr));
|
||||||
|
|
@ -210,73 +130,11 @@ Close an existing socket.
|
||||||
static int socket_close(lua_State *L) {
|
static int socket_close(lua_State *L) {
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
||||||
|
|
||||||
if (userdata->isSSL) {
|
|
||||||
sslcDestroyContext(&userdata->sslContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
closesocket(userdata->socket);
|
closesocket(userdata->socket);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Get some informations from a socket.
|
|
||||||
@function :getpeername
|
|
||||||
@treturn string IP
|
|
||||||
@treturn number port
|
|
||||||
*/
|
|
||||||
static int socket_getpeername(lua_State *L) {
|
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
|
||||||
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
socklen_t addrSize = sizeof(addr);
|
|
||||||
|
|
||||||
getpeername(userdata->socket, (struct sockaddr*)&addr, &addrSize);
|
|
||||||
|
|
||||||
lua_pushstring(L, inet_ntoa(addr.sin_addr));
|
|
||||||
lua_pushinteger(L, ntohs(addr.sin_port));
|
|
||||||
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Get some local informations from a socket.
|
|
||||||
@function :getsockname
|
|
||||||
@treturn string IP
|
|
||||||
@treturn number port
|
|
||||||
*/
|
|
||||||
static int socket_getsockname(lua_State *L) {
|
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
|
||||||
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
socklen_t addrSize = sizeof(addr);
|
|
||||||
|
|
||||||
getsockname(userdata->socket, (struct sockaddr*)&addr, &addrSize);
|
|
||||||
|
|
||||||
lua_pushstring(L, inet_ntoa(addr.sin_addr));
|
|
||||||
lua_pushinteger(L, ntohs(addr.sin_port));
|
|
||||||
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Set if the socket should be blocking.
|
|
||||||
@function :setBlocking
|
|
||||||
@tparam[opt=true] boolean block if `false`, the socket won't block
|
|
||||||
*/
|
|
||||||
static int socket_setBlocking(lua_State *L) {
|
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
|
||||||
bool block = true;
|
|
||||||
if (lua_isboolean(L, 2))
|
|
||||||
block = lua_toboolean(L, 2);
|
|
||||||
|
|
||||||
int flags = fcntl(userdata->socket, F_GETFL, 0);
|
|
||||||
flags = block?(flags&~O_NONBLOCK):(flags|O_NONBLOCK);
|
|
||||||
fcntl(userdata->socket, F_SETFL, flags);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
TCP Sockets
|
TCP Sockets
|
||||||
@section TCP
|
@section TCP
|
||||||
|
|
@ -293,7 +151,6 @@ static int socket_accept(lua_State *L) {
|
||||||
socket_userdata *client = lua_newuserdata(L, sizeof(*client));
|
socket_userdata *client = lua_newuserdata(L, sizeof(*client));
|
||||||
luaL_getmetatable(L, "LSocket");
|
luaL_getmetatable(L, "LSocket");
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
client->isSSL = false;
|
|
||||||
|
|
||||||
socklen_t addrSize = sizeof(client->addr);
|
socklen_t addrSize = sizeof(client->addr);
|
||||||
client->socket = accept(userdata->socket, (struct sockaddr*)&client->addr, &addrSize);
|
client->socket = accept(userdata->socket, (struct sockaddr*)&client->addr, &addrSize);
|
||||||
|
|
@ -310,7 +167,6 @@ Connect a socket to a server. The TCP object becomes a TCPClient object.
|
||||||
@function :connect
|
@function :connect
|
||||||
@tparam string host address of the host
|
@tparam string host address of the host
|
||||||
@tparam number port port of the server
|
@tparam number port port of the server
|
||||||
@tparam[opt=false] boolean ssl use SSL if `true`
|
|
||||||
@treturn[1] boolean true if success
|
@treturn[1] boolean true if success
|
||||||
@treturn[2] boolean false if failed
|
@treturn[2] boolean false if failed
|
||||||
@treturn[2] string error string
|
@treturn[2] string error string
|
||||||
|
|
@ -319,12 +175,11 @@ static int socket_connect(lua_State *L) {
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
||||||
char *addr = (char*)luaL_checkstring(L, 2);
|
char *addr = (char*)luaL_checkstring(L, 2);
|
||||||
int port = luaL_checkinteger(L, 3);
|
int port = luaL_checkinteger(L, 3);
|
||||||
bool ssl = lua_toboolean(L, 4);
|
|
||||||
|
|
||||||
userdata->host = gethostbyname(addr);
|
userdata->host = gethostbyname(addr);
|
||||||
if (userdata->host == NULL) {
|
if (userdata->host == NULL) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, strerror(errno));
|
lua_pushstring(L, "No such host");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,22 +188,10 @@ static int socket_connect(lua_State *L) {
|
||||||
|
|
||||||
if (connect(userdata->socket, (const struct sockaddr*)&userdata->addr, sizeof(userdata->addr)) < 0) {
|
if (connect(userdata->socket, (const struct sockaddr*)&userdata->addr, sizeof(userdata->addr)) < 0) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, strerror(errno));
|
lua_pushstring(L, "Connection failed");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ssl) { // SSL context setup
|
|
||||||
sslcCreateContext(&userdata->sslContext, userdata->socket, SSLCOPT_Default, addr);
|
|
||||||
sslcContextSetRootCertChain(&userdata->sslContext, rootCertChain);
|
|
||||||
if (R_FAILED(sslcStartConnection(&userdata->sslContext, NULL, NULL))) {
|
|
||||||
sslcDestroyContext(&userdata->sslContext);
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushstring(L, "SSL connection failed");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
userdata->isSSL = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, 1);
|
lua_pushboolean(L, 1);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -375,9 +218,7 @@ If no data is avaible, it returns an empty string (non-blocking).
|
||||||
"a" to receive everything,
|
"a" to receive everything,
|
||||||
"l" to receive the next line, skipping the end of line,
|
"l" to receive the next line, skipping the end of line,
|
||||||
"L" to receive the next line, keeping the end of line.
|
"L" to receive the next line, keeping the end of line.
|
||||||
@treturn[1] string data
|
@treturn string data
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] integer error code
|
|
||||||
*/
|
*/
|
||||||
static int socket_receive(lua_State *L) {
|
static int socket_receive(lua_State *L) {
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
||||||
|
|
@ -396,11 +237,7 @@ static int socket_receive(lua_State *L) {
|
||||||
luaL_buffinit(L, &b);
|
luaL_buffinit(L, &b);
|
||||||
|
|
||||||
char buff;
|
char buff;
|
||||||
if (!userdata->isSSL) {
|
|
||||||
while (recv(userdata->socket, &buff, 1, flags) > 0 && buff != '\n') luaL_addchar(&b, buff);
|
while (recv(userdata->socket, &buff, 1, flags) > 0 && buff != '\n') luaL_addchar(&b, buff);
|
||||||
} else {
|
|
||||||
while (!R_FAILED(sslcRead(&userdata->sslContext, &buff, 1, false)) && buff != '\n') luaL_addchar(&b, buff);
|
|
||||||
}
|
|
||||||
|
|
||||||
luaL_pushresult(&b);
|
luaL_pushresult(&b);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -410,11 +247,7 @@ static int socket_receive(lua_State *L) {
|
||||||
luaL_buffinit(L, &b);
|
luaL_buffinit(L, &b);
|
||||||
|
|
||||||
char buff;
|
char buff;
|
||||||
if (!userdata->isSSL) {
|
|
||||||
while (buff != '\n' && recv(userdata->socket, &buff, 1, flags) > 0) luaL_addchar(&b, buff);
|
while (buff != '\n' && recv(userdata->socket, &buff, 1, flags) > 0) luaL_addchar(&b, buff);
|
||||||
} else {
|
|
||||||
while (buff != '\n' && !R_FAILED(sslcRead(&userdata->sslContext, &buff, 1, false))) luaL_addchar(&b, buff);
|
|
||||||
}
|
|
||||||
|
|
||||||
luaL_pushresult(&b);
|
luaL_pushresult(&b);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -425,17 +258,7 @@ static int socket_receive(lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
char *buff = malloc(count+1);
|
char *buff = malloc(count+1);
|
||||||
int len;
|
int len = recv(userdata->socket, buff, count, flags);
|
||||||
if (!userdata->isSSL) {
|
|
||||||
len = recv(userdata->socket, buff, count, flags);
|
|
||||||
} else {
|
|
||||||
len = sslcRead(&userdata->sslContext, buff, count, false);
|
|
||||||
if (R_FAILED(len)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushinteger(L, len);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*(buff+len) = 0x0; // text end
|
*(buff+len) = 0x0; // text end
|
||||||
|
|
||||||
lua_pushstring(L, buff);
|
lua_pushstring(L, buff);
|
||||||
|
|
@ -446,32 +269,14 @@ static int socket_receive(lua_State *L) {
|
||||||
Send some data over the TCP socket.
|
Send some data over the TCP socket.
|
||||||
@function :send
|
@function :send
|
||||||
@tparam string data data to send
|
@tparam string data data to send
|
||||||
@treturn[1] number amount of data sent
|
@treturn number amount of data sent
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] integer/string error code/message
|
|
||||||
*/
|
*/
|
||||||
static int socket_send(lua_State *L) {
|
static int socket_send(lua_State *L) {
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
char *data = (char*)luaL_checklstring(L, 2, &size);
|
char *data = (char*)luaL_checklstring(L, 2, &size);
|
||||||
|
|
||||||
ssize_t sent;
|
size_t sent = send(userdata->socket, data, size, 0);
|
||||||
if (!userdata->isSSL) {
|
|
||||||
sent = send(userdata->socket, data, size, 0);
|
|
||||||
} else {
|
|
||||||
sent = sslcWrite(&userdata->sslContext, data, size);
|
|
||||||
if (R_FAILED(sent)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushinteger(L, sent);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sent < 0) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushstring(L, strerror(errno));
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushinteger(L, sent);
|
lua_pushinteger(L, sent);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -483,105 +288,90 @@ UDP sockets
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Receive a datagram from the UDP object.
|
Receive some data from a server.
|
||||||
@function :receivefrom
|
@function :receivefrom
|
||||||
@tparam[opt=8191] number count maximum amount of bytes to receive from the datagram. Must be lower than 8192.
|
@tparam number count amount of data to receive
|
||||||
@treturn[1] string data
|
@tparam string host host name
|
||||||
@treturn[1] string IP address of the sender
|
@tparam number port port
|
||||||
@treturn[1] integer port number of the sender
|
@treturn string data
|
||||||
@treturn[2] nil in case of error or no datagram to receive
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
*/
|
||||||
static int socket_receivefrom(lua_State *L) {
|
static int socket_receivefrom(lua_State *L) {
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
||||||
int count = luaL_optinteger(L, 2, 8191);
|
int count = luaL_checkinteger(L, 2);
|
||||||
|
size_t namesize = 0;
|
||||||
|
char *hostname = (char*)luaL_optlstring(L, 3, NULL, &namesize);
|
||||||
|
int port = luaL_optinteger(L, 4, 0);
|
||||||
|
|
||||||
struct sockaddr_in from;
|
struct sockaddr_in from = {0};
|
||||||
socklen_t addr_len;
|
if (hostname != NULL) { // For a server
|
||||||
|
struct hostent *hostinfo = gethostbyname(hostname);
|
||||||
char* buffer = calloc(1, count+1);
|
if (hostinfo == NULL) {
|
||||||
ssize_t n = recvfrom(userdata->socket, buffer, count, 0, (struct sockaddr *)&from, &addr_len);
|
|
||||||
|
|
||||||
if (n == 0) {
|
|
||||||
free(buffer);
|
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "nothing to receive");
|
return 1;
|
||||||
return 2;
|
}
|
||||||
|
from.sin_addr = *(struct in_addr*)hostinfo->h_addr;
|
||||||
} else if (n < 0) {
|
from.sin_port = htons(port);
|
||||||
free(buffer);
|
from.sin_family = AF_INET;
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushstring(L, strerror(n));
|
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* buffer = malloc(count+1);
|
||||||
|
int n = recvfrom(userdata->socket, buffer, count, 0, (struct sockaddr*)&from, NULL);
|
||||||
|
*(buffer+n) = 0x0;
|
||||||
|
|
||||||
lua_pushstring(L, buffer);
|
lua_pushstring(L, buffer);
|
||||||
|
if (hostname != NULL) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
lua_pushstring(L, inet_ntoa(from.sin_addr));
|
lua_pushstring(L, inet_ntoa(from.sin_addr));
|
||||||
lua_pushinteger(L, ntohs(from.sin_port));
|
lua_pushinteger(L, ntohs(from.sin_port));
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Send a datagram to the specified IP and port.
|
Send some data to a server.
|
||||||
@function :sendto
|
@function :sendto
|
||||||
@tparam string data data to send
|
@tparam string data data to send
|
||||||
@tparam string host IP/hostname of the recipient
|
@tparam string host host name
|
||||||
@tparam number port port number of the recipient
|
@tparam number port port
|
||||||
@treturn[1] boolean true in case of success
|
|
||||||
@treturn[2] boolean false in case of error
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
*/
|
||||||
static int socket_sendto(lua_State *L) {
|
static int socket_sendto(lua_State *L) {
|
||||||
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket");
|
||||||
size_t datasize;
|
size_t datasize = 0;
|
||||||
const char *data = luaL_checklstring(L, 2, &datasize);
|
char *data = (char*)luaL_checklstring(L, 2, &datasize);
|
||||||
const char *hostname = luaL_checkstring(L, 3);
|
size_t namesize = 0;
|
||||||
|
char *hostname = (char*)luaL_checklstring(L, 3, &namesize);
|
||||||
int port = luaL_checkinteger(L, 4);
|
int port = luaL_checkinteger(L, 4);
|
||||||
|
|
||||||
struct hostent *hostinfo = gethostbyname(hostname);
|
struct hostent *hostinfo = gethostbyname(hostname);
|
||||||
if (hostinfo == NULL) {
|
if (hostinfo == NULL) {
|
||||||
lua_pushboolean(L, false);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "unknown host");
|
return 1;
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
|
struct sockaddr_in to = {0};
|
||||||
struct sockaddr_in to;
|
|
||||||
to.sin_addr = *(struct in_addr*)hostinfo->h_addr;
|
to.sin_addr = *(struct in_addr*)hostinfo->h_addr;
|
||||||
to.sin_port = htons(port);
|
to.sin_port = htons(port);
|
||||||
to.sin_family = AF_INET;
|
to.sin_family = AF_INET;
|
||||||
|
|
||||||
ssize_t n = sendto(userdata->socket, data, datasize, 0, (struct sockaddr *)&to, sizeof(to));
|
sendto(userdata->socket, data, datasize, 0, (struct sockaddr*)&to, sizeof(to));
|
||||||
|
|
||||||
if (n < 0) {
|
return 0;
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushstring(L, strerror(n));
|
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_pushboolean(L, true);
|
// module functions
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Module functions
|
|
||||||
static const struct luaL_Reg socket_functions[] = {
|
static const struct luaL_Reg socket_functions[] = {
|
||||||
{"init", socket_init },
|
{"init", socket_init },
|
||||||
{"shutdown", socket_shutdown},
|
{"shutdown", socket_shutdown},
|
||||||
{"tcp", socket_tcp },
|
{"tcp", socket_tcp },
|
||||||
{"udp", socket_udp },
|
{"udp", socket_udp },
|
||||||
{ "addTrustedRootCA", socket_addTrustedRootCA },
|
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Object methods
|
// object
|
||||||
static const struct luaL_Reg socket_methods[] = {
|
static const struct luaL_Reg socket_methods[] = {
|
||||||
{"accept", socket_accept },
|
{"accept", socket_accept },
|
||||||
{"bind", socket_bind },
|
{"bind", socket_bind },
|
||||||
{"close", socket_close },
|
{"close", socket_close },
|
||||||
{ "setBlocking", socket_setBlocking },
|
|
||||||
{"__gc", socket_close },
|
{"__gc", socket_close },
|
||||||
{"connect", socket_connect },
|
{"connect", socket_connect },
|
||||||
{"listen", socket_listen },
|
{"listen", socket_listen },
|
||||||
|
|
@ -589,8 +379,6 @@ static const struct luaL_Reg socket_methods[] = {
|
||||||
{"receivefrom", socket_receivefrom},
|
{"receivefrom", socket_receivefrom},
|
||||||
{"send", socket_send },
|
{"send", socket_send },
|
||||||
{"sendto", socket_sendto },
|
{"sendto", socket_sendto },
|
||||||
{ "getpeername", socket_getpeername },
|
|
||||||
{ "getsockname", socket_getsockname },
|
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
154
source/texture.c
154
source/texture.c
|
|
@ -12,12 +12,6 @@ The `gfx.texture` module.
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include <stb_image.h>
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
||||||
#include <stb_image_write.h>
|
|
||||||
#include <png.h>
|
|
||||||
|
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
|
|
||||||
int getType(const char *name) {
|
int getType(const char *name) {
|
||||||
|
|
@ -38,14 +32,12 @@ int getType(const char *name) {
|
||||||
// module functions
|
// module functions
|
||||||
|
|
||||||
/***
|
/***
|
||||||
Load a texture from a file. Supported formats: PNG, JPEG, BMP, GIF, PSD, TGA, HDR, PIC, PNM.
|
Load a texture from a file. Supported formats: PNG, JPEG, BMP.
|
||||||
@function load
|
@function load
|
||||||
@tparam string path path to the image file
|
@tparam string path path to the image file
|
||||||
@tparam[opt=PLACE_RAM] number place where to put the loaded texture
|
@tparam[opt=PLACE_RAM] number place where to put the loaded texture
|
||||||
@tparam[opt=auto] number type type of the image. This is only used to force loading PNG, JPEG or BMP files with the sfil library; any value between 5 and 250 will force using the stbi library. Leave nil to autodetect the format.
|
@tparam[opt=auto] number type type of the image
|
||||||
@treturn[1] texture the loaded texture object
|
@treturn texture the loaded texture object
|
||||||
@treturn[2] nil in case of error
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
*/
|
||||||
static int texture_load(lua_State *L) {
|
static int texture_load(lua_State *L) {
|
||||||
const char *path = luaL_checkstring(L, 1);
|
const char *path = luaL_checkstring(L, 1);
|
||||||
|
|
@ -66,16 +58,10 @@ static int texture_load(lua_State *L) {
|
||||||
} else if (type==2) { //BMP
|
} else if (type==2) { //BMP
|
||||||
texture->texture = sfil_load_BMP_file(path, place); //appears to be broken right now.
|
texture->texture = sfil_load_BMP_file(path, place); //appears to be broken right now.
|
||||||
} else {
|
} else {
|
||||||
int w, h;
|
|
||||||
char* data = (char*)stbi_load(path, &w, &h, NULL, 4);
|
|
||||||
if (data == NULL) {
|
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "Can't open file");
|
lua_pushstring(L, "Bad type");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
texture->texture = sf2d_create_texture_mem_RGBA8(data, w, h, TEXFMT_RGBA8, place);
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture->texture == NULL) {
|
if (texture->texture == NULL) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
|
|
@ -127,24 +113,20 @@ Texture object
|
||||||
/***
|
/***
|
||||||
Draw a texture.
|
Draw a texture.
|
||||||
@function :draw
|
@function :draw
|
||||||
@tparam integer x X position
|
@tparam number x X position
|
||||||
@tparam integer y Y position
|
@tparam number y Y position
|
||||||
@tparam[opt=0.0] number rad rotation of the texture around the hotspot (in radians)
|
@tparam[opt=0.0] number rad rotation of the texture (in radians)
|
||||||
@tparam[opt=0.0] number hotspotX the hostpot X coordinate
|
|
||||||
@tparam[opt=0.0] number hotspotY the hostpot Y coordinate
|
|
||||||
*/
|
*/
|
||||||
static int texture_draw(lua_State *L) {
|
static int texture_draw(lua_State *L) {
|
||||||
texture_userdata *texture = luaL_checkudata(L, 1, "LTexture");
|
texture_userdata *texture = luaL_checkudata(L, 1, "LTexture");
|
||||||
int x = luaL_checkinteger(L, 2);
|
int x = luaL_checkinteger(L, 2);
|
||||||
int y = luaL_checkinteger(L, 3);
|
int y = luaL_checkinteger(L, 3);
|
||||||
float rad = luaL_optnumber(L, 4, 0.0f);
|
float rad = luaL_optnumber(L, 4, 0.0f);
|
||||||
float hotspotX = luaL_optnumber(L, 5, 0.0f);
|
|
||||||
float hotspotY = luaL_optnumber(L, 6, 0.0f);
|
|
||||||
|
|
||||||
if (rad == 0.0f && texture->scaleX == 1.0f && texture->scaleY == 1.0f && texture->blendColor == 0xffffffff) {
|
if (rad == 0.0f && texture->scaleX == 1.0f && texture->scaleY == 1.0f && texture->blendColor == 0xffffffff) {
|
||||||
sf2d_draw_texture(texture->texture, x - hotspotX, y - hotspotY);
|
sf2d_draw_texture(texture->texture, x, y);
|
||||||
} else {
|
} else {
|
||||||
sf2d_draw_texture_rotate_scale_hotspot_blend(texture->texture, x, y, rad, texture->scaleX, texture->scaleY, hotspotX, hotspotY, texture->blendColor);
|
sf2d_draw_texture_part_rotate_scale_blend(texture->texture, x, y, rad, 0, 0, texture->texture->width, texture->texture->height, texture->scaleX, texture->scaleY, texture->blendColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -153,15 +135,13 @@ static int texture_draw(lua_State *L) {
|
||||||
/***
|
/***
|
||||||
Draw a part of the texture
|
Draw a part of the texture
|
||||||
@function :drawPart
|
@function :drawPart
|
||||||
@tparam integer x X position
|
@tparam number x X position
|
||||||
@tparam integer y Y position
|
@tparam number y Y position
|
||||||
@tparam integer sx X position of the beginning of the part
|
@tparam number sx X position of the beginning of the part
|
||||||
@tparam integer sy Y position of the beginning of the part
|
@tparam number sy Y position of the beginning of the part
|
||||||
@tparam integer w width of the part
|
@tparam number w width of the part
|
||||||
@tparam integer h height of the part
|
@tparam number h height of the part
|
||||||
@tparam[opt=0.0] number rad rotation of the part around the hotspot (in radians)
|
@tparam[opt=0.0] number rad rotation of the part (in radians)
|
||||||
@tparam[opt=0.0] number hotspotX the hostpot X coordinate
|
|
||||||
@tparam[opt=0.0] number hotspotY the hostpot Y coordinate
|
|
||||||
*/
|
*/
|
||||||
static int texture_drawPart(lua_State *L) {
|
static int texture_drawPart(lua_State *L) {
|
||||||
texture_userdata *texture = luaL_checkudata(L, 1, "LTexture");
|
texture_userdata *texture = luaL_checkudata(L, 1, "LTexture");
|
||||||
|
|
@ -172,10 +152,8 @@ static int texture_drawPart(lua_State *L) {
|
||||||
int w = luaL_checkinteger(L, 6);
|
int w = luaL_checkinteger(L, 6);
|
||||||
int h = luaL_checkinteger(L, 7);
|
int h = luaL_checkinteger(L, 7);
|
||||||
int rad = luaL_optnumber(L, 8, 0.0f);
|
int rad = luaL_optnumber(L, 8, 0.0f);
|
||||||
float hotspotX = luaL_optnumber(L, 9, 0.0f);
|
|
||||||
float hotspotY = luaL_optnumber(L, 10, 0.0f);
|
|
||||||
|
|
||||||
sf2d_draw_texture_part_rotate_scale_hotspot_blend(texture->texture, x, y, rad, sx, sy, w, h, texture->scaleX, texture->scaleY, hotspotX, hotspotY, texture->blendColor);
|
sf2d_draw_texture_part_rotate_scale_blend(texture->texture, x, y, rad, sx, sy, w, h, texture->scaleX, texture->scaleY, texture->blendColor);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -214,12 +192,12 @@ static int texture_unload(lua_State *L) {
|
||||||
Rescale the texture. The default scale is `1.0`.
|
Rescale the texture. The default scale is `1.0`.
|
||||||
@function :scale
|
@function :scale
|
||||||
@tparam number scaleX new scale of the width
|
@tparam number scaleX new scale of the width
|
||||||
@tparam[opt=scaleX] number scaleY new scale of the height
|
@tparam number scaleY new scale of the height
|
||||||
*/
|
*/
|
||||||
static int texture_scale(lua_State *L) {
|
static int texture_scale(lua_State *L) {
|
||||||
texture_userdata *texture = luaL_checkudata(L, 1, "LTexture");
|
texture_userdata *texture = luaL_checkudata(L, 1, "LTexture");
|
||||||
float sx = luaL_checknumber(L, 2);
|
float sx = luaL_checknumber(L, 2);
|
||||||
float sy = luaL_optnumber(L, 3, sx);
|
float sy = luaL_checknumber(L, 3);
|
||||||
|
|
||||||
texture->scaleX = sx;
|
texture->scaleX = sx;
|
||||||
texture->scaleY = sy;
|
texture->scaleY = sy;
|
||||||
|
|
@ -276,98 +254,6 @@ static int texture_setBlendColor(lua_State *L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
Get the blend color of the texture.
|
|
||||||
@function :getBlendColor
|
|
||||||
@treturn number the blend color
|
|
||||||
*/
|
|
||||||
static int texture_getBlendColor(lua_State *L) {
|
|
||||||
texture_userdata *texture = luaL_checkudata(L, 1, "LTexture");
|
|
||||||
|
|
||||||
lua_pushinteger(L, texture->blendColor);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Save a texture to a file.
|
|
||||||
@function :save
|
|
||||||
@tparam string filename path to the file to save the texture to
|
|
||||||
@tparam[opt=TYPE_PNG] number type type of the image to save. Can be TYPE_PNG or TYPE_BMP
|
|
||||||
@treturn[1] boolean true on success
|
|
||||||
@treturn[2] boolean `false` in case of error
|
|
||||||
@treturn[2] string error message
|
|
||||||
*/
|
|
||||||
static int texture_save(lua_State *L) {
|
|
||||||
texture_userdata *texture = luaL_checkudata(L, 1, "LTexture");
|
|
||||||
const char* path = luaL_checkstring(L, 2);
|
|
||||||
u8 type = luaL_optinteger(L, 3, 0);
|
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
if (type == 0) { // PNG
|
|
||||||
FILE* file = fopen(path, "wb");
|
|
||||||
if (file == NULL) {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushstring(L, "Can open file");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
||||||
png_infop infos = png_create_info_struct(png);
|
|
||||||
setjmp(png_jmpbuf(png));
|
|
||||||
png_init_io(png, file);
|
|
||||||
|
|
||||||
png_set_IHDR(png, infos, texture->texture->width, texture->texture->height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
|
||||||
png_write_info(png, infos);
|
|
||||||
|
|
||||||
png_bytep row = malloc(4 * texture->texture->width * sizeof(png_byte));
|
|
||||||
|
|
||||||
for(int y=0;y<texture->texture->height;y++) {
|
|
||||||
for (int x=0;x<texture->texture->width;x++) {
|
|
||||||
((u32*)row)[x] = __builtin_bswap32(sf2d_get_pixel(texture->texture, x, y));
|
|
||||||
}
|
|
||||||
png_write_row(png, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_write_end(png, NULL);
|
|
||||||
|
|
||||||
fclose(file);
|
|
||||||
png_free_data(png, infos, PNG_FREE_ALL, -1);
|
|
||||||
png_destroy_write_struct(&png, &infos);
|
|
||||||
free(row);
|
|
||||||
|
|
||||||
result = 1;
|
|
||||||
|
|
||||||
} else if (type == 2) { // BMP
|
|
||||||
u32* buff = malloc(texture->texture->width * texture->texture->height * 4);
|
|
||||||
if (buff == NULL) {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushstring(L, "Failed to allocate buffer");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
for (int y=0;y<texture->texture->height;y++) {
|
|
||||||
for (int x=0;x<texture->texture->width;x++) {
|
|
||||||
buff[x+(y*texture->texture->width)] = __builtin_bswap32(sf2d_get_pixel(texture->texture, x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = stbi_write_bmp(path, texture->texture->width, texture->texture->height, 4, buff);
|
|
||||||
free(buff);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushstring(L, "Not a valid type");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == 0) {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushstring(L, "Failed to save the texture");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, true);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// object
|
// object
|
||||||
static const struct luaL_Reg texture_methods[] = {
|
static const struct luaL_Reg texture_methods[] = {
|
||||||
{ "draw", texture_draw },
|
{ "draw", texture_draw },
|
||||||
|
|
@ -378,8 +264,6 @@ static const struct luaL_Reg texture_methods[] = {
|
||||||
{ "getPixel", texture_getPixel },
|
{ "getPixel", texture_getPixel },
|
||||||
{ "setPixel", texture_setPixel },
|
{ "setPixel", texture_setPixel },
|
||||||
{ "setBlendColor", texture_setBlendColor },
|
{ "setBlendColor", texture_setBlendColor },
|
||||||
{ "getBlendColor", texture_getBlendColor },
|
|
||||||
{ "save", texture_save },
|
|
||||||
{ "__gc", texture_unload },
|
{ "__gc", texture_unload },
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
589
source/uds.c
589
source/uds.c
|
|
@ -1,589 +0,0 @@
|
||||||
/***
|
|
||||||
The `uds` module. Used for 3DS-to-3DS wireless communication.
|
|
||||||
The default wlancommID is 0x637472c2.
|
|
||||||
@module ctr.uds
|
|
||||||
@usage local uds = require("ctr.uds")
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <3ds/result.h>
|
|
||||||
#include <3ds/types.h>
|
|
||||||
#include <3ds/services/uds.h>
|
|
||||||
|
|
||||||
#include <lualib.h>
|
|
||||||
#include <lauxlib.h>
|
|
||||||
|
|
||||||
bool initStateUDS = false;
|
|
||||||
|
|
||||||
udsBindContext bind = {0};
|
|
||||||
udsNetworkStruct network = {0};
|
|
||||||
u8 data_channel = 1;
|
|
||||||
|
|
||||||
/***
|
|
||||||
Initialize the UDS module.
|
|
||||||
@function init
|
|
||||||
@tparam[opt=0x3000] number context size in bytes, must be a multiple of 0x1000
|
|
||||||
@tparam[opt=3DS username] string username UTF-8 username on the network
|
|
||||||
@treturn[1] boolean `true` on success
|
|
||||||
@treturn[2] boolean `false` on error
|
|
||||||
@treturn[2] number error code
|
|
||||||
*/
|
|
||||||
static int uds_init(lua_State *L) {
|
|
||||||
if (!initStateUDS) {
|
|
||||||
size_t memSize = luaL_optinteger(L, 1, 0x3000);
|
|
||||||
const char* username = luaL_optstring(L, 2, NULL);
|
|
||||||
|
|
||||||
Result ret = udsInit(memSize, username);
|
|
||||||
if (R_FAILED(ret)) {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushinteger(L, ret);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
initStateUDS = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, true);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Disable the UDS module.
|
|
||||||
@function shutdown
|
|
||||||
*/
|
|
||||||
static int uds_shutdown(lua_State *L) {
|
|
||||||
udsExit();
|
|
||||||
initStateUDS = false;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Scan for network beacons.
|
|
||||||
@function scan
|
|
||||||
@tparam[opt=0x637472c2] number commID application local-WLAN unique ID
|
|
||||||
@tparam[opt=0] number id8 additional ID to use different network types
|
|
||||||
@tparam[opt=all] string hostMAC if set, only scan networks from this MAC address (format: `XX:XX:XX:XX:XX:XX`, with hexadecimal values)
|
|
||||||
@treturn[1] table a table containing beacons objects
|
|
||||||
@treturn[2] nil
|
|
||||||
@treturn[2] number/string error code
|
|
||||||
*/
|
|
||||||
static int uds_scan(lua_State *L) {
|
|
||||||
static const size_t tmpbuffSize = 0x4000;
|
|
||||||
u32* tmpbuff = malloc(tmpbuffSize);
|
|
||||||
|
|
||||||
udsNetworkScanInfo* networks = NULL;
|
|
||||||
size_t totalNetworks = 0;
|
|
||||||
|
|
||||||
u32 wlanCommID = luaL_optinteger(L, 1, 0x637472c2);
|
|
||||||
u8 id8 = luaL_optinteger(L, 2, 0);
|
|
||||||
|
|
||||||
// MAC address conversion
|
|
||||||
const char* hostMACString = luaL_optstring(L, 3, NULL);
|
|
||||||
u8* hostMAC;
|
|
||||||
if (hostMACString != NULL) {
|
|
||||||
hostMAC = malloc(6*sizeof(u8));
|
|
||||||
unsigned int tmpMAC[6];
|
|
||||||
if (sscanf(hostMACString, "%x:%x:%x:%x:%x:%x", &tmpMAC[0], &tmpMAC[1], &tmpMAC[2], &tmpMAC[3], &tmpMAC[4], &tmpMAC[5]) != 6) {
|
|
||||||
free(tmpbuff);
|
|
||||||
free(hostMAC);
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushstring(L, "Bad MAC formating");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
for (int i=0;i<6;i++) {
|
|
||||||
hostMAC[i] = tmpMAC[i];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
hostMAC = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
udsConnectionStatus status;
|
|
||||||
udsGetConnectionStatus(&status);
|
|
||||||
Result ret = udsScanBeacons(tmpbuff, tmpbuffSize, &networks, &totalNetworks, wlanCommID, id8, hostMAC, (status.status!=0x03));
|
|
||||||
free(tmpbuff);
|
|
||||||
free(hostMAC);
|
|
||||||
if (R_FAILED(ret)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushinteger(L, ret);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the networks to a table of userdatas
|
|
||||||
lua_createtable(L, 0, totalNetworks);
|
|
||||||
for (int i=1;i<=totalNetworks;i++) {
|
|
||||||
udsNetworkScanInfo* beacon = lua_newuserdata(L, sizeof(udsNetworkScanInfo));
|
|
||||||
luaL_getmetatable(L, "LUDSBeaconScan");
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
memcpy(beacon, &networks[0], sizeof(udsNetworkScanInfo));
|
|
||||||
lua_seti(L, -3, i);
|
|
||||||
}
|
|
||||||
free(networks);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Check for data in the receive buffer.
|
|
||||||
@function available
|
|
||||||
@treturn boolean `true` if there is data to receive, `false` if not
|
|
||||||
*/
|
|
||||||
static int uds_available(lua_State *L) {
|
|
||||||
lua_pushboolean(L, udsWaitDataAvailable(&bind, false, false));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Return a packet from the receive buffer.
|
|
||||||
@function receive
|
|
||||||
@tparam[opt=maximum] number size maximum size of the data to receive
|
|
||||||
@treturn string data, can be an empty string
|
|
||||||
@treturn number source node, 0 if nothing has been received
|
|
||||||
*/
|
|
||||||
static int uds_receive(lua_State *L) {
|
|
||||||
size_t maxSize = luaL_optinteger(L, 1, UDS_DATAFRAME_MAXSIZE);
|
|
||||||
char* buff = malloc(maxSize);
|
|
||||||
if (buff == NULL) luaL_error(L, "Memory allocation error");
|
|
||||||
size_t received = 0;
|
|
||||||
u16 sourceID = 0;
|
|
||||||
|
|
||||||
Result ret = udsPullPacket(&bind, buff, maxSize, &received, &sourceID);
|
|
||||||
if (R_FAILED(ret)) {
|
|
||||||
free(buff);
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushinteger(L, ret);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushlstring(L, buff, received);
|
|
||||||
free(buff);
|
|
||||||
lua_pushinteger(L, sourceID);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Send a packet to a node.
|
|
||||||
@function send
|
|
||||||
@tparam string data data to send
|
|
||||||
@tparam[opt=BROADCAST] number nodeID nodeID to send the packet to
|
|
||||||
*/
|
|
||||||
static int uds_send(lua_State *L) {
|
|
||||||
size_t size = 0;
|
|
||||||
const char *buff = luaL_checklstring(L, 1, &size);
|
|
||||||
u16 nodeID = luaL_optinteger(L, 2, UDS_BROADCAST_NETWORKNODEID);
|
|
||||||
|
|
||||||
udsSendTo(nodeID, data_channel, 0, buff, size);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Return information about nodes in the networks
|
|
||||||
@function getNodesInfo
|
|
||||||
@treturn tablea table containing nodes informations, as tables (not userdatas).
|
|
||||||
A node table is like: `{ username = "azerty", nodeID = 12 }`
|
|
||||||
*/
|
|
||||||
static int uds_getNodesInfo(lua_State *L) {
|
|
||||||
lua_newtable(L);
|
|
||||||
for (int i=0;i<UDS_MAXNODES;i++) {
|
|
||||||
udsNodeInfo node;
|
|
||||||
udsGetNodeInformation(i, &node);
|
|
||||||
if (!udsCheckNodeInfoInitialized(&node)) continue;
|
|
||||||
lua_createtable(L, 0, 2);
|
|
||||||
char tmpstr[256];
|
|
||||||
memset(tmpstr, 0, sizeof(tmpstr));
|
|
||||||
udsGetNodeInfoUsername(&node, tmpstr);
|
|
||||||
lua_pushstring(L, tmpstr);
|
|
||||||
lua_setfield(L, -2, "username");
|
|
||||||
lua_pushinteger(L, node.NetworkNodeID);
|
|
||||||
lua_setfield(L, -2, "nodeID");
|
|
||||||
|
|
||||||
lua_seti(L, -3, i+1);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Return the application data of the current network.
|
|
||||||
@function getAppData
|
|
||||||
@tparam[opt=0x4000] number maxSize maximum application data size to return
|
|
||||||
@treturn string application data
|
|
||||||
*/
|
|
||||||
static int uds_getAppData(lua_State *L) {
|
|
||||||
size_t maxSize = luaL_optinteger(L, 1, 0x4000);
|
|
||||||
char* buff = malloc(maxSize);
|
|
||||||
if (buff == NULL) luaL_error(L, "Memory allocation error");
|
|
||||||
size_t size = 0;
|
|
||||||
|
|
||||||
udsGetApplicationData(buff, maxSize, &size);
|
|
||||||
|
|
||||||
lua_pushlstring(L, buff, size);
|
|
||||||
free(buff);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Client part
|
|
||||||
@section client
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***
|
|
||||||
Connect to a network.
|
|
||||||
@function connect
|
|
||||||
@tparam beaconScan beacon beacon to connect to
|
|
||||||
@tparam[opt=""] string passphrase passphrase for the network
|
|
||||||
@tparam[opt=CLIENT] number conType type of connection, can be `CONTYPE_CLIENT` or `CONTYPE_SPECTATOR`
|
|
||||||
@tparam[opt=BROADCAST] number nodeID nodeID to receive data from
|
|
||||||
@tparam[opt=default] number recvBuffSize size of the buffer that receives data
|
|
||||||
@tparam[opt=1] number dataChannel data channel of the network; this should be 1
|
|
||||||
@treturn[1] boolean `true` on success
|
|
||||||
@treturn[2] boolean `false` on error
|
|
||||||
@treturn[2] number error code
|
|
||||||
*/
|
|
||||||
static int uds_connect(lua_State *L) {
|
|
||||||
udsNetworkScanInfo* beacon = luaL_checkudata(L, 1, "LUDSBeaconScan");
|
|
||||||
const char* passphrase = luaL_optstring(L, 2, "");
|
|
||||||
size_t passphraseSize = strlen(passphrase)+1;
|
|
||||||
udsConnectionType connType = luaL_optinteger(L, 3, UDSCONTYPE_Client);
|
|
||||||
u16 recvNetworkNodeID = luaL_optinteger(L, 4, UDS_BROADCAST_NETWORKNODEID);
|
|
||||||
u32 recvBufferSize = luaL_optinteger(L, 5, UDS_DEFAULT_RECVBUFSIZE);
|
|
||||||
u8 dataChannel = luaL_optinteger(L, 6, 1);
|
|
||||||
|
|
||||||
Result ret = udsConnectNetwork(&beacon->network, passphrase, passphraseSize, &bind, recvNetworkNodeID, connType, dataChannel, recvBufferSize);
|
|
||||||
if (R_FAILED(ret)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushinteger(L, ret);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, true);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Disconnect from the network.
|
|
||||||
@function disconnect
|
|
||||||
*/
|
|
||||||
static int uds_disconnect(lua_State *L) {
|
|
||||||
udsDisconnectNetwork();
|
|
||||||
udsUnbind(&bind);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Server part
|
|
||||||
@section server
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***
|
|
||||||
Create a network.
|
|
||||||
@function createNetwork
|
|
||||||
@tparam[opt=""] string passphrase passphrase of the network
|
|
||||||
@tparam[opt=16] number maxNodes maximum number of nodes that can be connected to the network, including the host (max 16)
|
|
||||||
@tparam[opt=0x637472c2] number commID application local-WLAN unique ID
|
|
||||||
@tparam[opt=default] number recvBuffSize size of the buffer that receives data
|
|
||||||
@tparam[opt=1] number dataChannel data channel of the network; this should be 1
|
|
||||||
@treturn[1] boolean `true` on success
|
|
||||||
@treturn[2] boolean `false` on error
|
|
||||||
@treturn[2] number error code
|
|
||||||
*/
|
|
||||||
static int uds_createNetwork(lua_State *L) {
|
|
||||||
size_t passSize = 0;
|
|
||||||
const char *pass = luaL_optlstring(L, 1, "", &passSize);
|
|
||||||
u8 maxNodes = luaL_optinteger(L, 2, UDS_MAXNODES);
|
|
||||||
u32 commID = luaL_optinteger(L, 3, 0x637472c2);
|
|
||||||
u32 recvBuffSize = luaL_optinteger(L, 4, UDS_DEFAULT_RECVBUFSIZE);
|
|
||||||
u8 dataChannel = luaL_optinteger(L, 5, 1);
|
|
||||||
|
|
||||||
udsGenerateDefaultNetworkStruct(&network, commID, dataChannel, maxNodes);
|
|
||||||
Result ret = udsCreateNetwork(&network, pass, passSize, &bind, dataChannel, recvBuffSize);
|
|
||||||
if (R_FAILED(ret)) {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushinteger(L, ret);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Set the application data of the created network.
|
|
||||||
@function setAppData
|
|
||||||
@tparam string appData application data
|
|
||||||
@treturn[1] boolean `true` on success
|
|
||||||
@treturn[2] boolean `false` on error
|
|
||||||
@treturn[2] number error code
|
|
||||||
*/
|
|
||||||
static int uds_setAppData(lua_State *L) {
|
|
||||||
size_t size = 0;
|
|
||||||
const char* data = luaL_checklstring(L, 1, &size);
|
|
||||||
|
|
||||||
Result ret = udsSetApplicationData(data, size);
|
|
||||||
if (R_FAILED(ret)) {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_pushinteger(L, ret);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, true);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Destroy the network.
|
|
||||||
@function destroyNetwork
|
|
||||||
*/
|
|
||||||
static int uds_destroyNetwork(lua_State *L) {
|
|
||||||
udsDestroyNetwork();
|
|
||||||
udsUnbind(&bind);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Eject all the spectators connected to the network.
|
|
||||||
@function ejectSpectators
|
|
||||||
@tparam[opt=false] boolean reallow set to `true` to still allow the spectators to connect
|
|
||||||
*/
|
|
||||||
static int uds_ejectSpectators(lua_State *L) {
|
|
||||||
bool reallow = false;
|
|
||||||
if (lua_isboolean(L, 1))
|
|
||||||
reallow = lua_toboolean(L, 1);
|
|
||||||
|
|
||||||
udsEjectSpectator();
|
|
||||||
|
|
||||||
if (reallow)
|
|
||||||
udsAllowSpectators();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Eject a client connected to the network.
|
|
||||||
@function ejectClient
|
|
||||||
@tparam[opt=BROADCAST] number nodeID node ID of the client to eject, or `BROADCAST` for all the clients
|
|
||||||
*/
|
|
||||||
static int uds_ejectClient(lua_State *L) {
|
|
||||||
u16 nodeID = luaL_optinteger(L, 1, UDS_BROADCAST_NETWORKNODEID);
|
|
||||||
|
|
||||||
udsEjectClient(nodeID);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
beaconScan
|
|
||||||
@section beaconScan
|
|
||||||
*/
|
|
||||||
static const struct luaL_Reg beaconScan_methods[];
|
|
||||||
|
|
||||||
static int beaconScan___index(lua_State *L) {
|
|
||||||
udsNetworkScanInfo* beacon = luaL_checkudata(L, 1, "LUDSBeaconScan");
|
|
||||||
|
|
||||||
if (lua_isstring(L, 2)) {
|
|
||||||
const char* index = luaL_checkstring(L, 2);
|
|
||||||
/***
|
|
||||||
@tfield integer channel Wifi channel of the beacon
|
|
||||||
*/
|
|
||||||
if (!strcmp(index, "channel")) {
|
|
||||||
lua_pushinteger(L, beacon->datareply_entry.channel);
|
|
||||||
return 1;
|
|
||||||
/***
|
|
||||||
@tfield string mac MAC address of the beacon (`mac` for lowercase, `MAC` for uppercase)
|
|
||||||
@usage
|
|
||||||
beacon.mac -> ab:cd:ef:ab:cd:ef
|
|
||||||
beacon.MAC -> AB:CD:EF:AB:CD:EF
|
|
||||||
*/
|
|
||||||
} else if (!strcmp(index, "mac") && !strcmp(index, "MAC")) {
|
|
||||||
char* macString = malloc(18);
|
|
||||||
if (macString == NULL) luaL_error(L, "Out of memory");
|
|
||||||
if (index[1] == 'm') { // lowercase
|
|
||||||
sprintf(macString, "%0x2:%0x2:%0x2:%0x2:%0x2:%0x2", beacon->datareply_entry.mac_address[0], beacon->datareply_entry.mac_address[1], beacon->datareply_entry.mac_address[2], beacon->datareply_entry.mac_address[3], beacon->datareply_entry.mac_address[4], beacon->datareply_entry.mac_address[5]);
|
|
||||||
} else {
|
|
||||||
sprintf(macString, "%0X2:%0X2:%0X2:%0X2:%0X2:%0X2", beacon->datareply_entry.mac_address[0], beacon->datareply_entry.mac_address[1], beacon->datareply_entry.mac_address[2], beacon->datareply_entry.mac_address[3], beacon->datareply_entry.mac_address[4], beacon->datareply_entry.mac_address[5]);
|
|
||||||
}
|
|
||||||
lua_pushstring(L, macString);
|
|
||||||
free(macString);
|
|
||||||
return 1;
|
|
||||||
/***
|
|
||||||
@tfield table nodes a table containing nodes informations, as tables (not userdatas).
|
|
||||||
A node table is like: `{ username = "azerty", nodeID = 12 }`
|
|
||||||
*/
|
|
||||||
} else if (!strcmp(index, "nodes")) {
|
|
||||||
lua_newtable(L);
|
|
||||||
for (int i=0;i<UDS_MAXNODES;i++) {
|
|
||||||
if (!udsCheckNodeInfoInitialized(&beacon->nodes[i])) continue;
|
|
||||||
lua_createtable(L, 0, 2);
|
|
||||||
char tmpstr[256]; // 256 is maybe too much ... But we have a lot of RAM.
|
|
||||||
memset(tmpstr, 0, sizeof(tmpstr));
|
|
||||||
udsGetNodeInfoUsername(&beacon->nodes[i], tmpstr);
|
|
||||||
lua_pushstring(L, tmpstr);
|
|
||||||
lua_setfield(L, -2, "username");
|
|
||||||
lua_pushinteger(L, (&beacon->nodes[i])->NetworkNodeID);
|
|
||||||
lua_setfield(L, -2, "nodeID");
|
|
||||||
|
|
||||||
lua_seti(L, -3, i+1);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
/***
|
|
||||||
@tfield number id8 id8 of the beacon's network
|
|
||||||
*/
|
|
||||||
} else if (!strcmp(index, "id8")) {
|
|
||||||
lua_pushinteger(L, beacon->network.id8);
|
|
||||||
return 1;
|
|
||||||
/***
|
|
||||||
@tfield number networkID random ID of the network
|
|
||||||
*/
|
|
||||||
} else if (!strcmp(index, "networkID")) {
|
|
||||||
lua_pushinteger(L, beacon->network.networkID);
|
|
||||||
return 1;
|
|
||||||
/***
|
|
||||||
@tfield boolean allowSpectators `true` if new spectators are allowed on the network
|
|
||||||
*/
|
|
||||||
} else if (!strcmp(index, "allowSpectators")) {
|
|
||||||
lua_pushboolean(L, !(beacon->network.attributes&UDSNETATTR_DisableConnectSpectators));
|
|
||||||
return 1;
|
|
||||||
/***
|
|
||||||
@tfield boolean allowClients `true` if new clients are allowed on the network
|
|
||||||
*/
|
|
||||||
} else if (!strcmp(index, "allowClients")) {
|
|
||||||
lua_pushboolean(L, !(beacon->network.attributes&UDSNETATTR_DisableConnectClients));
|
|
||||||
return 1;
|
|
||||||
// methods
|
|
||||||
} else {
|
|
||||||
for (int i=0;beaconScan_methods[i].name;i++) {
|
|
||||||
if (!strcmp(beaconScan_methods[i].name, index)) {
|
|
||||||
lua_pushcfunction(L, beaconScan_methods[i].func);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
Return the application data of the beacon
|
|
||||||
@function :getAppData
|
|
||||||
@tparam[opt=0x4000] number maxSize maximum application data size to return
|
|
||||||
@treturn[1] string application data
|
|
||||||
@treturn[2] nil
|
|
||||||
@treturn[2] error code
|
|
||||||
*/
|
|
||||||
static int beaconScan_getAppData(lua_State *L) {
|
|
||||||
udsNetworkScanInfo* beacon = luaL_checkudata(L, 1, "LUDSBeaconScan");
|
|
||||||
size_t maxSize = luaL_optinteger(L, 2, 0x4000);
|
|
||||||
|
|
||||||
u8* data = malloc(maxSize);
|
|
||||||
if (data == NULL) luaL_error(L, "Memory allocation error");
|
|
||||||
|
|
||||||
size_t size = 0;
|
|
||||||
udsGetNetworkStructApplicationData(&beacon->network, data, maxSize, &size);
|
|
||||||
|
|
||||||
lua_pushlstring(L, (const char*)data, size);
|
|
||||||
free(data);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// beaconScan object
|
|
||||||
static const struct luaL_Reg beaconScan_methods[] = {
|
|
||||||
{"__index", beaconScan___index },
|
|
||||||
{"getAppData", beaconScan_getAppData},
|
|
||||||
{NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
// module functions
|
|
||||||
static const struct luaL_Reg uds_lib[] = {
|
|
||||||
{"init", uds_init },
|
|
||||||
{"shutdown", uds_shutdown },
|
|
||||||
{"scan", uds_scan },
|
|
||||||
{"getNodesInfo ", uds_getNodesInfo },
|
|
||||||
{"getAppData", uds_getAppData },
|
|
||||||
{"connect", uds_connect },
|
|
||||||
{"available", uds_available },
|
|
||||||
{"send", uds_send },
|
|
||||||
{"receive", uds_receive },
|
|
||||||
{"disconnect", uds_disconnect },
|
|
||||||
{"createNetwork", uds_createNetwork },
|
|
||||||
{"setAppData", uds_setAppData },
|
|
||||||
{"destroyNetwork", uds_destroyNetwork },
|
|
||||||
{"ejectSpectators", uds_ejectSpectators},
|
|
||||||
{"ejectClient", uds_ejectClient },
|
|
||||||
{NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
/***
|
|
||||||
Constants
|
|
||||||
@section constants
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct { char *name; int value; } uds_constants[] = {
|
|
||||||
/***
|
|
||||||
@field BROADCAST broadcast node ID
|
|
||||||
*/
|
|
||||||
{"BROADCAST", UDS_BROADCAST_NETWORKNODEID},
|
|
||||||
/***
|
|
||||||
@field HOST host node ID
|
|
||||||
*/
|
|
||||||
{"HOST", UDS_HOST_NETWORKNODEID },
|
|
||||||
/***
|
|
||||||
@field CLIENT used to specify a connection as a client
|
|
||||||
*/
|
|
||||||
{"CLIENT", UDSCONTYPE_Client },
|
|
||||||
/***
|
|
||||||
@field SPECTATOR used to specify a connection as a spectator
|
|
||||||
*/
|
|
||||||
{"SPECTATOR", UDSCONTYPE_Spectator },
|
|
||||||
{NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
int luaopen_uds_lib(lua_State *L) {
|
|
||||||
luaL_newmetatable(L, "LUDSBeaconScan");
|
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
luaL_setfuncs(L, beaconScan_methods, 0);
|
|
||||||
|
|
||||||
luaL_newlib(L, uds_lib);
|
|
||||||
|
|
||||||
for (int i = 0; uds_constants[i].name; i++) {
|
|
||||||
lua_pushinteger(L, uds_constants[i].value);
|
|
||||||
lua_setfield(L, -2, uds_constants[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void load_uds_lib(lua_State *L) {
|
|
||||||
luaL_requiref(L, "ctr.uds", luaopen_uds_lib, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unload_uds_lib(lua_State *L) {
|
|
||||||
if (initStateUDS) {
|
|
||||||
udsConnectionStatus status;
|
|
||||||
udsGetConnectionStatus(&status);
|
|
||||||
switch (status.status) {
|
|
||||||
case 0x6: // connected as host
|
|
||||||
udsDestroyNetwork();
|
|
||||||
udsUnbind(&bind);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x9: // connected as client
|
|
||||||
case 0xA: // connected as spectator
|
|
||||||
udsDisconnectNetwork();
|
|
||||||
udsUnbind(&bind);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
udsExit();
|
|
||||||
initStateUDS = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue