mirror of
https://github.com/grumpycoders/pcsx-redux.git
synced 2026-10-12 02:15:13 +00:00
Creating the psyqo-lua directory.
- Added project files - Created Lua C++ wrapper - Adding hello-world style example
This commit is contained in:
@@ -10,8 +10,8 @@ cd $CWD
|
||||
|
||||
# Need to delete submodules actively.
|
||||
# Done in two passes for speed.
|
||||
git filter-branch -f --tree-filter 'find third_party -depth -not -name uC-sdk -and -not -path third_party/EABase -and -not -path third_party/EABase/\* -and -not -path third_party/EASTL -and -not -path third_party/EASTL/\* -delete || true' --tag-name-filter cat --prune-empty
|
||||
git filter-branch -f --tree-filter 'find third_party -depth -not -name uC-sdk -and -not -path third_party/EABase -and -not -path third_party/EABase/\* -and -not -path third_party/EASTL -and -not -path third_party/EASTL/\* -exec git rm -f {} \; || true' --tag-name-filter cat --prune-empty
|
||||
git filter-branch -f --tree-filter 'find third_party -depth -not -name uC-sdk -and -not -path third_party/psxlua -and -not -path third_party/EABase -and -not -path third_party/EABase/\* -and -not -path third_party/EASTL -and -not -path third_party/EASTL/\* -delete || true' --tag-name-filter cat --prune-empty
|
||||
git filter-branch -f --tree-filter 'find third_party -depth -not -name uC-sdk -and -not -path third_party/psxlua -and -not -path third_party/EABase -and -not -path third_party/EABase/\* -and -not -path third_party/EASTL -and -not -path third_party/EASTL/\* -exec git rm -f {} \; || true' --tag-name-filter cat --prune-empty
|
||||
|
||||
# Shuffle files around.
|
||||
git filter-branch -f --tree-filter 'mkdir -p src/mips ; git mv third_party src/mips ; mv .gitmodules src/mips ; mv docker* src/mips || true' --tag-name-filter cat --prune-empty
|
||||
@@ -33,5 +33,9 @@ git filter-branch -f --tree-filter "find . -name Makefile -exec sed 's|\.\./\.\.
|
||||
git filter-branch -f --tree-filter "find . -name '*.mk' -exec sed 's|\.\./\.\./\.\./third_party/EABase/|../third_party/EABase/|' -i {} \;" --tag-name-filter cat --prune-empty
|
||||
git filter-branch -f --tree-filter "find . -name '*.mk' -exec sed 's|\.\./\.\./\.\./third_party/EASTL/|../third_party/EASTL/|' -i {} \;" --tag-name-filter cat --prune-empty
|
||||
|
||||
# Adjust paths for psxlua
|
||||
git filter-branch -f --tree-filter "find . -name Makefile -exec sed 's|\.\./\.\./\.\./third_party/psxlua/|../third_party/psxlua/|' -i {} \;" --tag-name-filter cat --prune-empty
|
||||
git filter-branch -f --tree-filter "find . -name '*.mk' -exec sed 's|\.\./\.\./\.\./third_party/psxlua/|../third_party/psxlua/|' -i {} \;" --tag-name-filter cat --prune-empty
|
||||
|
||||
# Delete unwanted files
|
||||
git filter-branch -f --tree-filter 'find . -name compile_flags.txt -or -name Doxyfile -delete || true' --tag-name-filter cat --prune-empty
|
||||
|
||||
@@ -97,3 +97,6 @@
|
||||
[submodule "third_party/PEGTL"]
|
||||
path = third_party/PEGTL
|
||||
url = https://github.com/taocpp/PEGTL.git
|
||||
[submodule "third_party/psxlua"]
|
||||
path = third_party/psxlua
|
||||
url = https://github.com/grumpycoders/psxlua.git
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
PSYQOLUADIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
TARGET = psyqo-lua
|
||||
TYPE = library
|
||||
|
||||
SRCS = \
|
||||
src/lua.cpp \
|
||||
|
||||
CPPFLAGS += -I$(PSYQOLUADIR)../../../third_party/psxlua/src
|
||||
|
||||
EXTRA_DEPS += $(PSYQOLUADIR)Makefile
|
||||
|
||||
include ../psyqo/psyqo.mk
|
||||
@@ -0,0 +1,49 @@
|
||||
# PSYQo Lua
|
||||
|
||||
PSYQo Lua is a component of the PCSX-Redux project that integrates the Lua scripting language with PSYQo applications. It provides a streamlined way to leverage Lua's powerful scripting capabilities in your PlayStation applications.
|
||||
|
||||
## Features
|
||||
|
||||
### Automatic Lua VM Integration
|
||||
|
||||
PSYQo Lua automatically links the Lua virtual machine into your project, handling all the necessary build configurations and dependencies, while setting up the necessary glue between the Lua VM and the rest of PSYQo.
|
||||
|
||||
### C++ Wrapper for Lua API
|
||||
|
||||
The library provides an idiomatic C++ wrapper around the standard Lua C API, offering a modern C++ interfaces that reduce boilerplate code typically associated with Lua scripting.
|
||||
|
||||
### Example Implementation
|
||||
|
||||
A complete working example is provided in the [examples/hello](examples/hello) folder, demonstrating:
|
||||
|
||||
- How to initialize the Lua environment
|
||||
- Loading and executing Lua scripts
|
||||
- Exchanging data between C++ and Lua
|
||||
- Registering C++ functions for use in Lua scripts
|
||||
|
||||
## Getting Started
|
||||
|
||||
To use PSYQo Lua in your project:
|
||||
|
||||
1. Include the PSYQo Lua library in your Makefile:
|
||||
```makefile
|
||||
include path/to/psyqo-lua/psyqo-lua.mk
|
||||
```
|
||||
|
||||
1. Include the necessary headers:
|
||||
```cpp
|
||||
#include "psyqo-lua/lua.hh"
|
||||
```
|
||||
|
||||
2. Create a Lua VM in your application:
|
||||
```cpp
|
||||
psyqo::Lua L;
|
||||
```
|
||||
|
||||
3. Start using Lua in your PlayStation application with the C++ wrapper:
|
||||
```cpp
|
||||
L.loadBuffer("print('Hello, PSYQo Lua!')");
|
||||
L.pcall(0, 0);
|
||||
```
|
||||
|
||||
See the example in [examples/hello](examples/hello) for a more detailed implementation.
|
||||
@@ -0,0 +1,9 @@
|
||||
-m32
|
||||
-std=c++20
|
||||
-fcoroutines-ts
|
||||
-I.
|
||||
-I..
|
||||
-I../../../third_party/EASTL/include
|
||||
-I../../../third_party/EABase/include/Common
|
||||
-I../../../third_party/psxlua/src
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
TARGET = hello
|
||||
TYPE = ps-exe
|
||||
|
||||
SRCS = \
|
||||
hello.cpp \
|
||||
|
||||
ifeq ($(TEST),true)
|
||||
CPPFLAGS = -Werror
|
||||
endif
|
||||
CXXFLAGS = -std=c++20
|
||||
|
||||
include ../../psyqo-lua.mk
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 PCSX-Redux authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include "EASTL/string.h"
|
||||
#include "common/syscalls/syscalls.h"
|
||||
#include "psyqo-lua/lua.hh"
|
||||
#include "psyqo/application.hh"
|
||||
#include "psyqo/font.hh"
|
||||
#include "psyqo/gpu.hh"
|
||||
#include "psyqo/scene.hh"
|
||||
|
||||
namespace {
|
||||
|
||||
// Simple Lua script for demonstration
|
||||
constexpr const char DEMO_SCRIPT[] = R"(
|
||||
-- Simple Lua script to show psyqo::Lua functionality
|
||||
function factorial(n)
|
||||
if n <= 1 then
|
||||
return 1
|
||||
else
|
||||
return n * factorial(n - 1)
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate some values
|
||||
results = {
|
||||
factorial = factorial(5),
|
||||
message = 'Hello from Lua!',
|
||||
number = 42,
|
||||
table = {1, 2, 3, 'testing'}
|
||||
}
|
||||
|
||||
-- Call our C++ function
|
||||
greet 'Lua script'
|
||||
|
||||
return results
|
||||
)";
|
||||
|
||||
// C++ function to be called from Lua
|
||||
int luaGreet(psyqo::Lua L) {
|
||||
// Get the name parameter from Lua
|
||||
const char* name = L.checkString(1);
|
||||
|
||||
// Print to debug output (if available)
|
||||
ramsyscall_printf("Greeting from C++: Hello, %s!\n", name);
|
||||
|
||||
// No return values
|
||||
return 0;
|
||||
}
|
||||
|
||||
class LuaExample final : public psyqo::Application {
|
||||
void prepare() override;
|
||||
void createScene() override;
|
||||
|
||||
public:
|
||||
psyqo::Font<> m_font;
|
||||
psyqo::Lua L;
|
||||
|
||||
// Results from Lua execution
|
||||
eastl::string m_luaMessage;
|
||||
int m_luaNumber = 0;
|
||||
int m_luaFactorial = 0;
|
||||
bool m_luaSuccess = false;
|
||||
};
|
||||
|
||||
class LuaExampleScene final : public psyqo::Scene {
|
||||
void frame() override;
|
||||
};
|
||||
|
||||
LuaExample luaExample;
|
||||
LuaExampleScene luaExampleScene;
|
||||
|
||||
} // namespace
|
||||
|
||||
void LuaExample::prepare() {
|
||||
psyqo::GPU::Configuration config;
|
||||
config.set(psyqo::GPU::Resolution::W320)
|
||||
.set(psyqo::GPU::VideoMode::AUTO)
|
||||
.set(psyqo::GPU::ColorMode::C15BITS)
|
||||
.set(psyqo::GPU::Interlace::PROGRESSIVE);
|
||||
gpu().initialize(config);
|
||||
|
||||
// Initialize Lua and register our functions
|
||||
L.push(luaGreet);
|
||||
L.setGlobal("greet");
|
||||
|
||||
// Execute our demo script
|
||||
if (L.loadBuffer(DEMO_SCRIPT, "demo") == 0) {
|
||||
// Script loaded successfully, now execute it
|
||||
if (L.pcall(0, 1) == 0) {
|
||||
// Script executed successfully
|
||||
m_luaSuccess = true;
|
||||
|
||||
// Get the results table
|
||||
if (L.isTable(-1)) {
|
||||
// Extract factorial result
|
||||
L.getField(-1, "factorial");
|
||||
if (L.isNumber(-1)) {
|
||||
m_luaFactorial = L.toNumber(-1);
|
||||
}
|
||||
L.pop();
|
||||
|
||||
// Extract message
|
||||
L.getField(-1, "message");
|
||||
if (L.isString(-1)) {
|
||||
m_luaMessage = L.toString(-1);
|
||||
}
|
||||
L.pop();
|
||||
|
||||
// Extract number
|
||||
L.getField(-1, "number");
|
||||
if (L.isNumber(-1)) {
|
||||
m_luaNumber = L.toNumber(-1);
|
||||
}
|
||||
L.pop();
|
||||
}
|
||||
|
||||
// Clean stack
|
||||
L.pop();
|
||||
} else {
|
||||
// Script execution failed, get error message
|
||||
m_luaMessage = "Error: ";
|
||||
m_luaMessage += L.isString(-1) ? L.toString(-1) : "Unknown error";
|
||||
L.pop();
|
||||
}
|
||||
} else {
|
||||
// Script loading failed, get error message
|
||||
m_luaMessage = "Error loading script: ";
|
||||
m_luaMessage += L.isString(-1) ? L.toString(-1) : "Unknown error";
|
||||
L.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void LuaExample::createScene() {
|
||||
m_font.uploadSystemFont(gpu());
|
||||
pushScene(&luaExampleScene);
|
||||
}
|
||||
|
||||
void LuaExampleScene::frame() {
|
||||
auto& gpu = luaExample.gpu();
|
||||
auto& font = luaExample.m_font;
|
||||
psyqo::Color textColor = {.r = 255, .g = 255, .b = 255};
|
||||
|
||||
// Clear the screen with a nice background color
|
||||
gpu.clear({{.r = 0, .g = 64, .b = 91}});
|
||||
|
||||
// Display title
|
||||
font.print(gpu, "PSYQo Lua Example", {{.x = 16, .y = 32}}, textColor);
|
||||
|
||||
if (luaExample.m_luaSuccess) {
|
||||
// Display results from Lua execution
|
||||
font.print(gpu, "Lua script executed successfully!", {{.x = 16, .y = 64}}, textColor);
|
||||
font.printf(gpu, {{.x = 16, .y = 80}}, textColor, "Message from Lua: %s", luaExample.m_luaMessage.c_str());
|
||||
font.printf(gpu, {{.x = 16, .y = 96}}, textColor, "Number from Lua: %d", luaExample.m_luaNumber);
|
||||
font.printf(gpu, {{.x = 16, .y = 112}}, textColor, "Factorial(5) from Lua: %d", luaExample.m_luaFactorial);
|
||||
|
||||
// Example of calling Lua function from C++
|
||||
luaExample.L.getGlobal("factorial");
|
||||
luaExample.L.pushNumber(7); // Calculate factorial of 7
|
||||
if (luaExample.L.pcall(1, 1) != 0) {
|
||||
// Error occurred
|
||||
font.print(gpu, "Error calling factorial(7):", {{.x = 16, .y = 128}}, textColor);
|
||||
font.print(gpu, luaExample.L.toString(-1), {{.x = 16, .y = 144}}, textColor);
|
||||
} else {
|
||||
int factorial7 = luaExample.L.toNumber(-1);
|
||||
font.printf(gpu, {{.x = 16, .y = 144}}, textColor, "Calling factorial(7) from C++: %d", factorial7);
|
||||
}
|
||||
luaExample.L.pop();
|
||||
} else {
|
||||
// Display error message
|
||||
font.print(gpu, "Lua script execution failed:", {{.x = 16, .y = 64}}, textColor);
|
||||
font.print(gpu, luaExample.m_luaMessage.c_str(), {{.x = 16, .y = 80}}, textColor);
|
||||
}
|
||||
}
|
||||
|
||||
int main() { return luaExample.run(); }
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 PCSX-Redux authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include "lauxlib.h"
|
||||
#include "lua.h"
|
||||
}
|
||||
|
||||
#include "EASTL/string_view.h"
|
||||
#include "psyqo/fixed-point.hh"
|
||||
|
||||
namespace psyqo {
|
||||
|
||||
struct Lua {
|
||||
using lua_CPPFunction = int (*)(Lua);
|
||||
|
||||
Lua();
|
||||
Lua(lua_State* L) : L(L) {}
|
||||
Lua(Lua&& oL) noexcept : L(oL.L) { oL.L = nullptr; }
|
||||
Lua(const Lua& oL) : L(oL.L) {}
|
||||
|
||||
Lua& operator=(Lua&& oL) noexcept;
|
||||
|
||||
void close();
|
||||
|
||||
// Get the lua state, for use in C functions when a wrapper method isn't available
|
||||
lua_State* getState() { return L; }
|
||||
|
||||
// Stack Manipulation
|
||||
int getTop() { return lua_gettop(L); }
|
||||
void setTop(int idx) { lua_settop(L, idx); }
|
||||
void pop(int n = 1) { lua_settop(L, -(n)-1); }
|
||||
void clearStack() { lua_settop(L, 0); }
|
||||
void remove(int idx) { lua_remove(L, idx); }
|
||||
void insert(int idx) { lua_insert(L, idx); }
|
||||
void replace(int idx) { lua_replace(L, idx); }
|
||||
void copy(int fromidx, int toidx) { lua_copy(L, fromidx, toidx); }
|
||||
void copy(int fromidx) { lua_pushvalue(L, fromidx); }
|
||||
void checkStack(int sz, const char* msg = nullptr) { luaL_checkstack(L, sz, msg); }
|
||||
int getabsolute(int index) {
|
||||
if ((index < 0) && (index > LUA_REGISTRYINDEX)) return getTop() + index + 1;
|
||||
return index;
|
||||
}
|
||||
|
||||
// Push values
|
||||
void push() { lua_pushnil(L); }
|
||||
void push(bool b) { lua_pushboolean(L, b); }
|
||||
void pushNumber(lua_Number n) { lua_pushnumber(L, n); }
|
||||
void push(std::nullptr_t) { lua_pushlightuserdata(L, nullptr); }
|
||||
void push(const char* s, size_t len) { lua_pushlstring(L, s, len); }
|
||||
template <size_t S>
|
||||
void push(const char (&s)[S]) {
|
||||
lua_pushlstring(L, s, S - 1);
|
||||
}
|
||||
void push(eastl::string_view s) { lua_pushlstring(L, s.data(), s.size()); }
|
||||
void push(const char* fmt, ...);
|
||||
void push(lua_CFunction f, int closure = 0) { lua_pushcclosure(L, f, closure); }
|
||||
void push(lua_CPPFunction f);
|
||||
void push(void* p) { lua_pushlightuserdata(L, p); }
|
||||
void pushGlobalTable() { lua_pushglobaltable(L); }
|
||||
|
||||
// FixedPoint operations
|
||||
void push(FixedPoint<> fp);
|
||||
FixedPoint<> toFixedPoint(int idx);
|
||||
bool isFixedPoint(int idx);
|
||||
FixedPoint<> checkFixedPoint(int idx);
|
||||
FixedPoint<> optFixedPoint(int idx, FixedPoint<> def = {});
|
||||
|
||||
// Get values
|
||||
bool toBoolean(int idx) { return lua_toboolean(L, idx); }
|
||||
lua_Number toNumber(int idx, bool* isnum = nullptr);
|
||||
const char* toString(int idx, size_t* len = nullptr) { return lua_tolstring(L, idx, len); }
|
||||
size_t rawLen(int idx) { return lua_rawlen(L, idx); }
|
||||
lua_CFunction toCFunction(int idx) { return lua_tocfunction(L, idx); }
|
||||
void* toUserdata(int idx) { return lua_touserdata(L, idx); }
|
||||
lua_State* toThread(int idx) { return lua_tothread(L, idx); }
|
||||
const void* toPointer(int idx) { return lua_topointer(L, idx); }
|
||||
|
||||
// Type checking
|
||||
int type(int idx) { return lua_type(L, idx); }
|
||||
const char* typeName(int tp) { return lua_typename(L, tp); }
|
||||
bool isNil(int idx) { return lua_isnil(L, idx); }
|
||||
bool isNone(int idx) { return lua_isnone(L, idx); }
|
||||
bool isNoneOrNil(int idx) { return lua_isnoneornil(L, idx); }
|
||||
bool isBoolean(int idx) { return lua_isboolean(L, idx); }
|
||||
bool isNumber(int idx) { return lua_isnumber(L, idx); }
|
||||
bool isString(int idx) { return lua_isstring(L, idx); }
|
||||
bool isTable(int idx) { return lua_istable(L, idx); }
|
||||
bool isFunction(int idx) { return lua_isfunction(L, idx); }
|
||||
bool isCFunction(int idx) { return lua_iscfunction(L, idx); }
|
||||
bool isUserdata(int idx) { return lua_isuserdata(L, idx); }
|
||||
bool isLightUserdata(int idx) { return lua_islightuserdata(L, idx); }
|
||||
bool isThread(int idx) { return lua_isthread(L, idx); }
|
||||
|
||||
// Table operations
|
||||
void newTable() { lua_newtable(L); }
|
||||
void createTable(int narr, int nrec) { lua_createtable(L, narr, nrec); }
|
||||
void getTable(int idx) { lua_gettable(L, idx); }
|
||||
void getField(int idx, const char* k) { lua_getfield(L, idx, k); }
|
||||
void getGlobal(const char* name) { lua_getglobal(L, name); }
|
||||
void setTable(int idx) { lua_settable(L, idx); }
|
||||
void setField(int idx, const char* k) { lua_setfield(L, idx, k); }
|
||||
void setGlobal(const char* name) { lua_setglobal(L, name); }
|
||||
void rawGet(int idx) { lua_rawget(L, idx); }
|
||||
void rawGetI(int idx, int n) { lua_rawgeti(L, idx, n); }
|
||||
void rawGetP(int idx, const void* p) { lua_rawgetp(L, idx, p); }
|
||||
void rawSet(int idx) { lua_rawset(L, idx); }
|
||||
void rawSetI(int idx, int n) { lua_rawseti(L, idx, n); }
|
||||
void rawSetP(int idx, const void* p) { lua_rawsetp(L, idx, p); }
|
||||
int next(int idx) { return lua_next(L, idx); }
|
||||
|
||||
// Metatable operations
|
||||
int newMetatable(const char* tname) { return luaL_newmetatable(L, tname); }
|
||||
void getMetatable(const char* tname) { luaL_getmetatable(L, tname); }
|
||||
int getMetatable(int objindex) { return lua_getmetatable(L, objindex); }
|
||||
int setMetatable(int objindex) { return lua_setmetatable(L, objindex); }
|
||||
|
||||
// Function calling
|
||||
void call(int nargs, int nresults) { lua_call(L, nargs, nresults); }
|
||||
int pcall(int nargs, int nresults, int errfunc = 0) { return lua_pcall(L, nargs, nresults, errfunc); }
|
||||
|
||||
// Loading and executing
|
||||
int loadBuffer(const char* buff, size_t sz, const char* chunkname = nullptr) {
|
||||
return luaL_loadbuffer(L, buff, sz, chunkname);
|
||||
}
|
||||
template <size_t S>
|
||||
int loadBuffer(const char (&buff)[S], const char* chunkname = nullptr) {
|
||||
return luaL_loadbuffer(L, buff, S - 1, chunkname);
|
||||
}
|
||||
|
||||
// Memory management
|
||||
void* newUserdata(size_t sz) { return lua_newuserdata(L, sz); }
|
||||
|
||||
// Reference system
|
||||
int ref(int t = LUA_REGISTRYINDEX) { return luaL_ref(L, t); }
|
||||
void unref(int t, int ref) { return luaL_unref(L, t, ref); }
|
||||
|
||||
// Garbage collection
|
||||
int gc(int what, int data = 0) { return lua_gc(L, what, data); }
|
||||
|
||||
// Error handling and helpers
|
||||
int error(const char* fmt, ...);
|
||||
int argError(int narg, const char* extramsg) { return luaL_argerror(L, narg, extramsg); }
|
||||
void checkType(int narg, int t) { luaL_checktype(L, narg, t); }
|
||||
void checkAny(int narg) { luaL_checkany(L, narg); }
|
||||
lua_Number checkNumber(int narg) { return luaL_checknumber(L, narg); }
|
||||
lua_Number optNumber(int narg, lua_Number def) { return luaL_optnumber(L, narg, def); }
|
||||
const char* checkString(int narg) { return luaL_checkstring(L, narg); }
|
||||
const char* optString(int narg, const char* def) { return luaL_optstring(L, narg, def); }
|
||||
const char* checkLString(int narg, size_t* len) { return luaL_checklstring(L, narg, len); }
|
||||
const char* optLString(int narg, const char* def, size_t* len) { return luaL_optlstring(L, narg, def, len); }
|
||||
void* checkUdata(int narg, const char* tname) { return luaL_checkudata(L, narg, tname); }
|
||||
void argCheck(bool cond, int narg, const char* extramsg) { luaL_argcheck(L, cond, narg, extramsg); }
|
||||
int checkOption(int narg, const char* def, const char* const lst[]) { return luaL_checkoption(L, narg, def, lst); }
|
||||
bool checkBoolean(int narg) {
|
||||
luaL_checktype(L, narg, LUA_TBOOLEAN);
|
||||
return lua_toboolean(L, narg);
|
||||
}
|
||||
bool optBoolean(int narg, bool def) { return lua_isnoneornil(L, narg) ? def : lua_toboolean(L, narg); }
|
||||
|
||||
// Debug interface
|
||||
int getStack(int level, lua_Debug* ar) { return lua_getstack(L, level, ar); }
|
||||
int getInfo(const char* what, lua_Debug* ar) { return lua_getinfo(L, what, ar); }
|
||||
const char* getLocal(const lua_Debug* ar, int n) { return lua_getlocal(L, ar, n); }
|
||||
const char* setLocal(const lua_Debug* ar, int n) { return lua_setlocal(L, ar, n); }
|
||||
|
||||
// Coroutine functions
|
||||
int yield(int nresults) { return lua_yield(L, nresults); }
|
||||
int resume(Lua from, int narg) { return lua_resume(L, from.L, narg); }
|
||||
int status() { return lua_status(L); }
|
||||
|
||||
private:
|
||||
lua_State* L;
|
||||
void setupFixedPointMetatable();
|
||||
};
|
||||
|
||||
} // namespace psyqo
|
||||
@@ -0,0 +1,28 @@
|
||||
ifndef PSYQOLUADIR
|
||||
PSYQOLUADIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
LIBRARIES += $(PSYQOLUADIR)libpsyqo-lua.a $(PSYQOLUADIR)../../../third_party/psxlua/src/liblua.a
|
||||
|
||||
CPPFLAGS += -I$(PSYQOLUADIR)../../../third_party/psxlua/src
|
||||
|
||||
LDFLAGS += \
|
||||
-Wl,--defsym,luaI_sprintf=sprintf_for_Lua \
|
||||
-Wl,--defsym,luaI_realloc=psyqo_realloc \
|
||||
-Wl,--defsym,luaI_free=psyqo_free \
|
||||
|
||||
EXTRA_CLEAN += clean-psyqo-lua
|
||||
|
||||
include $(PSYQOLUADIR)../psyqo/psyqo.mk
|
||||
|
||||
$(PSYQOLUADIR)libpsyqo-lua.a:
|
||||
$(MAKE) -C $(PSYQOLUADIR) BUILD=$(BUILD)
|
||||
|
||||
$(PSYQOLUADIR)../../../third_party/psxlua/src/liblua.a:
|
||||
$(MAKE) -C $(PSYQOLUADIR)../../../third_party/psxlua psx
|
||||
|
||||
clean-psyqo-lua:
|
||||
$(MAKE) -C $(PSYQOLUADIR) clean
|
||||
$(MAKE) -C $(PSYQOLUADIR)../../../third_party/psxlua clean
|
||||
|
||||
.PHONY: clean-psyqo-lua $(PSYQOLUADIR)libpsyqo-lua.a $(PSYQOLUADIR)../../../third_party/psxlua/src/liblua.a
|
||||
endif
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 PCSX-Redux authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include "psyqo-lua/lua.hh"
|
||||
|
||||
#include "psyqo/kernel.hh"
|
||||
#include "psyqo/xprintf.h"
|
||||
|
||||
extern "C" {
|
||||
int sprintf_for_Lua(char* buf, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int ret = vsprintf(buf, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr char PSYQO_FIXEDPOINT_METATABLE[] = "psyqo.FixedPoint";
|
||||
|
||||
// FixedPoint implementation
|
||||
|
||||
void psyqo::Lua::push(FixedPoint<> fp) {
|
||||
// Get the FixedPoint constructor from registry
|
||||
getMetatable(PSYQO_FIXEDPOINT_METATABLE);
|
||||
getField(-1, "new");
|
||||
pushNumber(fp.raw());
|
||||
call(1, 1); // Create new FixedPoint table
|
||||
remove(-2); // Remove metatable from stack
|
||||
}
|
||||
|
||||
void psyqo::Lua::push(lua_CPPFunction f) {
|
||||
lua_pushlightuserdata(L, reinterpret_cast<void*>(f));
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State* L) -> int {
|
||||
lua_CPPFunction f = reinterpret_cast<lua_CPPFunction>(lua_touserdata(L, lua_upvalueindex(1)));
|
||||
psyqo::Lua lua(L);
|
||||
return f(lua);
|
||||
},
|
||||
1);
|
||||
}
|
||||
|
||||
// Check if value at index is a FixedPoint
|
||||
bool psyqo::Lua::isFixedPoint(int idx) {
|
||||
if (!isTable(idx)) return false;
|
||||
|
||||
// Check if it has a _raw field and the correct metatable
|
||||
getMetatable(idx);
|
||||
getMetatable(PSYQO_FIXEDPOINT_METATABLE);
|
||||
bool result = lua_rawequal(L, -1, -2);
|
||||
pop(2); // Pop both metatables
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get FixedPoint from stack at index idx
|
||||
psyqo::FixedPoint<> psyqo::Lua::toFixedPoint(int idx) {
|
||||
if (!isFixedPoint(idx)) return {};
|
||||
|
||||
getField(idx, "_raw");
|
||||
intptr_t raw = toNumber(-1);
|
||||
pop(1);
|
||||
|
||||
return FixedPoint<>(static_cast<int32_t>(raw), FixedPoint<>::RAW);
|
||||
}
|
||||
|
||||
// Check and get FixedPoint (with error if not FixedPoint)
|
||||
psyqo::FixedPoint<> psyqo::Lua::checkFixedPoint(int idx) {
|
||||
if (!isFixedPoint(idx)) {
|
||||
error("Expected FixedPoint, got %s", typeName(idx));
|
||||
return {};
|
||||
}
|
||||
|
||||
getField(idx, "_raw");
|
||||
intptr_t raw = toNumber(-1);
|
||||
pop(1);
|
||||
|
||||
return FixedPoint<>(static_cast<int32_t>(raw), FixedPoint<>::RAW);
|
||||
}
|
||||
|
||||
// Optional FixedPoint (returns default if not present or nil)
|
||||
psyqo::FixedPoint<> psyqo::Lua::optFixedPoint(int idx, FixedPoint<> def) {
|
||||
if (isNoneOrNil(idx)) return def;
|
||||
return checkFixedPoint(idx);
|
||||
}
|
||||
|
||||
psyqo::Lua::Lua() : L(luaL_newstate()) {
|
||||
static_assert(sizeof(Lua) == sizeof(lua_State*));
|
||||
Kernel::assert(L, "Couldn't create Lua VM");
|
||||
lua_atpanic(L, [](lua_State* L) -> int {
|
||||
const char* errorMsg = lua_tolstring(L, 1, nullptr);
|
||||
Kernel::abort(errorMsg);
|
||||
});
|
||||
setupFixedPointMetatable();
|
||||
}
|
||||
|
||||
psyqo::Lua& psyqo::Lua::operator=(Lua&& oL) noexcept {
|
||||
if (this == &oL) return *this;
|
||||
|
||||
Kernel::assert(!L, "Can't assign a Lua VM to another one.");
|
||||
|
||||
L = oL.L;
|
||||
oL.L = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void psyqo::Lua::close() {
|
||||
Kernel::assert(L, "Can't close an already closed VM");
|
||||
|
||||
lua_close(L);
|
||||
L = nullptr;
|
||||
}
|
||||
|
||||
lua_Number psyqo::Lua::toNumber(int idx, bool* isnum) {
|
||||
int success;
|
||||
lua_Number n = lua_tonumberx(L, idx, &success);
|
||||
if (isnum) *isnum = success;
|
||||
return n;
|
||||
}
|
||||
|
||||
int psyqo::Lua::error(const char* fmt, ...) {
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
luaL_where(L, 1);
|
||||
lua_pushvfstring(L, fmt, argp);
|
||||
va_end(argp);
|
||||
lua_concat(L, 2);
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
void psyqo::Lua::push(const char* fmt, ...) {
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
lua_pushvfstring(L, fmt, argp);
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
void psyqo::Lua::setupFixedPointMetatable() {
|
||||
// Check if metatable already exists
|
||||
if (newMetatable(PSYQO_FIXEDPOINT_METATABLE) == 0) {
|
||||
// Metatable already exists, just pop it
|
||||
pop(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create C++ implemented methods for operations that are more complex
|
||||
push([](psyqo::Lua L) {
|
||||
if (!L.isFixedPoint(1)) {
|
||||
return L.error("First argument must be a FixedPoint");
|
||||
}
|
||||
|
||||
// Get the first FixedPoint
|
||||
auto fp1 = L.toFixedPoint(1);
|
||||
|
||||
if (L.isNumber(2)) {
|
||||
// FixedPoint * number
|
||||
lua_Number n = L.checkNumber(2);
|
||||
auto result = fp1 * n;
|
||||
L.push(result);
|
||||
} else if (L.isFixedPoint(2)) {
|
||||
// FixedPoint * FixedPoint
|
||||
auto fp2 = L.toFixedPoint(2);
|
||||
auto result = fp1 * fp2;
|
||||
L.push(result);
|
||||
} else {
|
||||
return L.error("Cannot multiply FixedPoint by this type");
|
||||
}
|
||||
|
||||
return 1;
|
||||
});
|
||||
setField(1, "__mul");
|
||||
|
||||
push([](psyqo::Lua L) {
|
||||
if (!L.isFixedPoint(1)) {
|
||||
return L.error("First argument must be a FixedPoint");
|
||||
}
|
||||
|
||||
// Get the first FixedPoint
|
||||
auto fp1 = L.toFixedPoint(1);
|
||||
|
||||
if (L.isNumber(2)) {
|
||||
// FixedPoint / number
|
||||
lua_Number n = L.checkNumber(2);
|
||||
if (n == 0) return L.error("Division by zero");
|
||||
|
||||
auto result = fp1 / n;
|
||||
L.push(result);
|
||||
} else if (L.isFixedPoint(2)) {
|
||||
// FixedPoint / FixedPoint
|
||||
auto fp2 = L.toFixedPoint(2);
|
||||
if (fp2.raw() == 0) return L.error("Division by zero");
|
||||
|
||||
auto result = fp1 / fp2;
|
||||
L.push(result);
|
||||
} else {
|
||||
return L.error("Cannot divide FixedPoint by this type");
|
||||
}
|
||||
|
||||
return 1;
|
||||
});
|
||||
setField(1, "__div");
|
||||
|
||||
push([](psyqo::Lua L) {
|
||||
if (!L.isFixedPoint(1)) {
|
||||
return L.error("Expected FixedPoint table");
|
||||
}
|
||||
|
||||
auto fp = L.toFixedPoint(1);
|
||||
int32_t raw = fp.raw();
|
||||
|
||||
// Convert to simple string representation
|
||||
int integer = raw >> 12;
|
||||
unsigned fraction = raw & 0xfff;
|
||||
|
||||
if (fraction == 0) {
|
||||
L.push("%d", integer);
|
||||
} else {
|
||||
unsigned decimal = (fraction * 1000) >> 12;
|
||||
L.push("%d.%03u", integer, decimal);
|
||||
}
|
||||
|
||||
return 1;
|
||||
});
|
||||
setField(1, "__tostring");
|
||||
|
||||
// We'll use an embedded Lua script to define most of the FixedPoint functionality
|
||||
static const char fixedPointScript[] = R"lua(
|
||||
return function(metatable)
|
||||
FixedPoint = metatable
|
||||
|
||||
-- Simple operations can be done directly in Lua
|
||||
function FixedPoint.__add(a, b)
|
||||
local raw_a = a._raw
|
||||
if type(b) == "number" then
|
||||
-- FixedPoint + number (treated as integer)
|
||||
return FixedPoint.new(raw_a + bit.lshift(b, 12))
|
||||
elseif type(b) == "table" and b._raw then
|
||||
-- FixedPoint + FixedPoint
|
||||
return FixedPoint.new(raw_a + b._raw)
|
||||
else
|
||||
error("Cannot add FixedPoint to this type")
|
||||
end
|
||||
end
|
||||
|
||||
function FixedPoint.__sub(a, b)
|
||||
local raw_a = a._raw
|
||||
if type(b) == "number" then
|
||||
-- FixedPoint - number (treated as integer)
|
||||
return FixedPoint.new(raw_a - bit.lshift(b, 12))
|
||||
elseif type(b) == "table" and b._raw then
|
||||
-- FixedPoint - FixedPoint
|
||||
return FixedPoint.new(raw_a - b._raw)
|
||||
else
|
||||
error("Cannot subtract this type from FixedPoint")
|
||||
end
|
||||
end
|
||||
|
||||
function FixedPoint.__unm(a)
|
||||
-- Unary minus
|
||||
return FixedPoint.new(-a._raw)
|
||||
end
|
||||
|
||||
function FixedPoint.__eq(a, b)
|
||||
if type(b) == "table" and b._raw then
|
||||
return a._raw == b._raw
|
||||
elseif type(b) == "number" then
|
||||
-- Compare with an integer number (shifted)
|
||||
return a._raw == bit.lshift(b, 12)
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function FixedPoint.__lt(a, b)
|
||||
if type(b) == "table" and b._raw then
|
||||
return a._raw < b._raw
|
||||
elseif type(b) == "number" then
|
||||
-- Compare with an integer number (shifted)
|
||||
return a._raw < bit.lshift(b, 12)
|
||||
else
|
||||
error("Cannot compare FixedPoint with this type")
|
||||
end
|
||||
end
|
||||
|
||||
function FixedPoint.__le(a, b)
|
||||
if type(b) == "table" and b._raw then
|
||||
return a._raw <= b._raw
|
||||
elseif type(b) == "number" then
|
||||
-- Compare with an integer number (shifted)
|
||||
return a._raw <= bit.lshift(b, 12)
|
||||
else
|
||||
error("Cannot compare FixedPoint with this type")
|
||||
end
|
||||
end
|
||||
|
||||
-- Get raw value
|
||||
function FixedPoint:raw()
|
||||
return self._raw
|
||||
end
|
||||
|
||||
-- Method to convert to a simple number (for simple calculations)
|
||||
function FixedPoint:toNumber()
|
||||
return (self._raw + 2048) / 4096
|
||||
end
|
||||
|
||||
-- Create a new FixedPoint from raw value
|
||||
function FixedPoint.new(raw_value)
|
||||
return setmetatable({_raw = raw_value}, FixedPoint)
|
||||
end
|
||||
end
|
||||
)lua";
|
||||
|
||||
// Load the Lua script
|
||||
if (loadBuffer(fixedPointScript, sizeof(fixedPointScript) - 1) != 0) {
|
||||
const char* errorMsg = toString(2);
|
||||
psyqo::Kernel::abort(errorMsg);
|
||||
}
|
||||
|
||||
// Get the function
|
||||
if (pcall(0, 1) != 0) {
|
||||
const char* errorMsg = toString(2);
|
||||
psyqo::Kernel::abort(errorMsg);
|
||||
}
|
||||
|
||||
// And call it with the metatable for finishing the setup
|
||||
copy(1);
|
||||
if (pcall(1, 0) != 0) {
|
||||
const char* errorMsg = toString(2);
|
||||
psyqo::Kernel::abort(errorMsg);
|
||||
}
|
||||
|
||||
pop();
|
||||
}
|
||||
+1
Submodule third_party/psxlua added at 3439ab8636
Reference in New Issue
Block a user