Skip to content
Snippets Groups Projects
Commit fdb8ec6f authored by dg's avatar dg
Browse files

Added sqlite3 support

git-svn-id: http://svn.net-core.org/repos/t-engine4@978 51575b47-30f0-44d4-a5cc-537603b46e54
parent 5540c71d
No related branches found
No related tags found
No related merge requests found
......@@ -179,6 +179,12 @@ function _M:selectStepMain()
}, self.w * 0.3, self.h * 0.2, self.w * 0.4, self.h * 0.3)
self.step:setKeyHandling()
self.step:setMouseHandling()
self.step.key:addBinds{
-- Lua console
LUA_CONSOLE = function()
print("plop") self:registerDialog(require("engine.DebugConsole").new())
end,
}
end
function _M:selectStepNew()
......
--[[--------------------------------------------------------------------------
Author: Michael Roth <mroth@nessie.de>
Copyright (c) 2004, 2005 Michael Roth <mroth@nessie.de>
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.
--]]--------------------------------------------------------------------------
--[[
TODO:
collation set_collation(name, func)
collation_needed set_collation_handler(func)
progress_handler set_progress_handler(func)
authorizer set_auth_handler(func)
commit_hook set_commit_handler(func)
--]]
require "libluasqlite3-loader"
local api, ERR, TYPE, AUTH = load_libluasqlite3()
local db_class = { }
local stmt_class = { }
local function check_stmt(stmt)
assert(type(stmt.handles) == "table", "Prepared statement expected")
return stmt
end
local function check_single_stmt(stmt)
assert(type(stmt.handles) == "table" and table.getn(stmt.handles) == 1, "Single prepared statement expected")
return stmt.handles[1]
end
local function check_db(db)
assert(db.handle, "Open database handle expected")
return db
end
local function check_table(tab, msg)
assert(type(tab) == "table", msg)
return tab
end
local function check_string(str, msg)
assert( type(str) == "string", msg )
return str
end
local function check_number(num, msg)
assert( type(num) == "number", msg )
return num
end
-----------------------------------------------------
-- Error test und report helper for sqlite3 errors --
-----------------------------------------------------
local function is_error(status)
return not ( status == ERR.OK or status == ERR.ROW or status == ERR.DONE )
end
local function is_row(status)
return status == ERR.ROW
end
local function is_done(status)
return status == ERR.DONE
end
local function errmsg(db_handle)
return api.errmsg(db_handle) or "Unknown error"
end
-------------------------------------------------------------------------
-- Creates an oject. An object is a table with itself set as metatable --
-------------------------------------------------------------------------
local function object()
local t = { }
setmetatable(t, t)
return t
end
-----------------------------------
-- Registry for tables (objects) --
-----------------------------------
local function create_registry()
return { 0 }
end
local function register(registry, object)
local id = registry[1]
if id == 0 then
table.insert(registry, object)
return table.getn(registry)
else
registry[1] = registry[id]
registry[id] = object
return id
end
end
local function unregister(registry, id)
registry[id] = registry[1]
registry[1] = id
end
-------------
-- sqlite3 --
-------------
sqlite3 = { }
function sqlite3.open(filename)
check_string(filename, "Filename as string expected")
local status, handle = api.open(filename)
if is_error(status) then
local errmsg = errmsg(handle)
api.close(handle)
return nil, errmsg
end
local db = object()
db.__gc = db_class.close
db.__index = db_class
db.filename = filename
db.handle = handle
db.stmts = create_registry()
return db
end
function sqlite3.open_memory()
return sqlite3.open(":memory:")
end
--------------------
-- Database Class --
--------------------
function db_class.close(db)
check_db(db)
for _, obj in ipairs(db.stmts) do
if type(obj) == "table" then
obj:close()
end
end
local status = api.close(db.handle)
if is_error(status) then
return nil, errmsg(db.handle)
end
db.handle = nil
db.stmts = nil
db.__gc = nil
return db
end
function db_class.interrupt(db)
check_db(db)
local status = api.interrupt(db.handle)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.last_insert_rowid(db)
check_db(db)
return api.last_insert_rowid(db.handle)
end
function db_class.changes(db)
check_db(db)
return api.changes(db.handle)
end
function db_class.total_changes(db)
check_db(db)
return api.total_changes(db.handle)
end
function db_class.exec(db, sql)
check_db(db)
check_string(sql)
local status = api.exec(db.handle, sql)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.irows(db, sql, tab)
check_db(db)
return db:prepare(sql):irows(tab, true)
end
function db_class.rows(db, sql, tab)
check_db(db)
return db:prepare(sql):rows(tab, true)
end
function db_class.cols(db, sql)
check_db(db)
return db:prepare(sql):cols(true)
end
function db_class.first_irow(db, sql, tab)
check_db(db)
return db:prepare(sql):first_irow(tab, true)
end
function db_class.first_row(db, sql, tab)
check_db(db)
return db:prepare(sql):first_row(tab, true)
end
function db_class.first_cols(db, sql)
check_db(db)
return db:prepare(sql):first_cols(true)
end
function db_class.prepare(db, paranames, sql)
check_db(db)
if sql == nil then
sql = paranames
paranames = nil
end
check_string(sql, "db:prepare: SQL statement as string expected")
local function cleanup(handles)
for _, handle in ipairs(handles) do
api.finalize(handle)
end
end
local function count_parameters(handles)
local parameter_count = 0
for _, handle in ipairs(handles) do
parameter_count = parameter_count + api.bind_parameter_count(handle)
end
return parameter_count
end
local function build_handles(sql)
local status, handle
local remaining = sql
local handles = { }
while remaining do
status, handle, remaining = api.prepare(db.handle, remaining)
if is_error(status) then
local errmsg = errmsg(db.handle)
cleanup(handles)
return nil, errmsg
end
table.insert(handles, handle)
end
return handles
end
local function anonymous_parameters(handles)
for _, handle in ipairs(handles) do
for i = 1, api.bind_parameter_count(handle) do
if api.bind_parameter_name_x(handle, i) then
return false
end
end
end
return true
end
local function named_parameters(handles)
for _, handle in ipairs(handles) do
for i = 1, api.bind_parameter_count(handle) do
if not api.bind_parameter_name_x(handle, i) then
return false
end
end
end
return true
end
local function create_mapping(handles, paranames)
local invers = { }
for index, name in ipairs(paranames) do
invers[name] = index
end
local mapping = { }
for _, handle in ipairs(handles) do
for index = 1, api.bind_parameter_count(handle) do
local parameter_name = api.bind_parameter_name_x(handle, index)
local pos = invers[parameter_name]
if pos == nil then
cleanup(handles)
return nil, "db:prepare: Unknown parameter name '" .. parameter_name .. "'in statement."
end
table.insert(mapping, pos)
end
end
return mapping
end
local function collect_parameter_names(handles)
local seen = { }
local names = { }
for _, handle in ipairs(handles) do
for index = 1, api.bind_parameter_count(handle) do
local parameter_name = api.bind_parameter_name_x(handle, index)
if not seen[parameter_name] then
table.insert(names, parameter_name)
seen[parameter_name] = true
end
end
end
return names
end
local function fix_parameter_names(unfixed_parameters)
local fixed_parameters = { }
for _, unfixed_name in ipairs(unfixed_parameters) do
local _, _, fixed_name = string.find(unfixed_name, "^[:$]?(%a%w*)$")
if not fixed_name then
return nil, "db:prepare: Invalid parameter name: '" .. unfixed_name .."'."
end
table.insert(fixed_parameters, fixed_name)
end
return fixed_parameters
end
local function create_stmt(db, handles, parameter_count)
local stmt = object()
stmt.__gc = stmt_class.close
stmt.__index = stmt_class
stmt.handles = handles
stmt.db = db
stmt.reg_id = register(db.stmts, stmt)
stmt.parameter_count= parameter_count
return stmt
end
local handles, errmsg = build_handles(sql)
if errmsg then
return nil, errmsg
end
local parameter_count = count_parameters(handles)
if parameter_count == 0 then -- No parameters at all
return create_stmt(db, handles, 0)
else
if anonymous_parameters(handles) then -- All parameters are anonymous ("?")
return create_stmt(db, handles, parameter_count)
elseif named_parameters(handles) then -- All parameters are named (":foobar" & "$foobar")
if paranames then -- Fixed mapping of parameter names
check_table(paranames, "db:prepare: Names of parameters expected as strings")
local fixed_parameter_names, errmsg = fix_parameter_names(paranames)
if errmsg then
cleanup(handles)
return nil, errmgs
end
local mapping, errmsg = create_mapping(handles, fixed_parameter_names)
if errmsg then
cleanup(handles)
return nil, errmsg
end
local stmt = create_stmt(db, handles, table.getn(fixed_parameter_names))
stmt.mapping = mapping
stmt.paranames = fixed_parameter_names
return stmt
else -- Automatic mapping of paramter names
local parameter_names = collect_parameter_names(handles)
local mapping = create_mapping(handles, parameter_names)
local stmt = create_stmt(db, handles, table.getn(parameter_names))
stmt.mapping = mapping
stmt.paranames = parameter_names
return stmt
end
else -- Mixed paramters are not allowed
cleanup(handles)
return nil, "db:prepare: Mixed anonymous and named parameters are not allowed."
end
end
end
local function call_user_func(context, func, num_values, values)
-- Don't use table.insert() because of nils in lua-5.1
local arg = { }
for index = 1, num_values do
arg[index] = api.value(values, index-1)
end
-- Make lua-5.0.2 unpack() happy
arg.n = num_values
-- lua-5.1 unpack() style / lua-5.0.2 ignores additional arguments
local ok, result = pcall(func, unpack(arg, 1, num_values))
if not ok then
api.result_error(context, tostring(result))
else
api.result(context, result)
end
end
function db_class.set_function(db, name, num_args, func)
check_db(db)
local function xfunc(context, num_values, values)
call_user_func(context, func, num_values, values)
end
local status = api.create_function(db.handle, name, num_args, xfunc, nil, nil)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.set_aggregate(db, name, num_args, create_funcs)
check_db(db)
local step, final
local function xstep(context, num_values, values)
if not step and not final then
step, final = create_funcs()
end
call_user_func(context, step, num_values, values)
end
local function xfinal(context)
local ok, result = pcall(final, api.aggregate_count(context))
if not ok then
api.result_error(context, tostring(result))
else
api.result(context, result)
end
end
local status = api.create_function(db.handle, name, num_args, nil, xstep, xfinal)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.set_trace_handler(db, func)
check_db(db)
local status = api.trace(db.handle, func)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.set_busy_timeout(db, ms)
check_db(db)
local status = api.busy_timeout(db.handle, ms)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.set_busy_handler(db, func)
check_db(db)
local status = api.busy_handler(db.handle, func)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
---------------------
-- Statement Class --
---------------------
function stmt_class.bind(stmt, ...)
local function bind_with_mapping(parameters)
local mapping = stmt.mapping
local map_index = 1
for _, handle in ipairs(stmt.handles) do
for index = 1, api.bind_parameter_count(handle) do
local status = api.bind(handle, index, parameters[mapping[map_index]])
if is_error(status) then
return nil, errmsg(stmt.db.handle)
end
map_index = map_index + 1
end
end
end
local function bind_without_mapping(parameters)
local parameter_index = 1
for _, handle in ipairs(stmt.handles) do
for index = 1, api.bind_parameter_count(handle) do
local status = api.bind(handle, index, parameters[parameter_index])
if is_error(status) then
return nil, errmsg(stmt.db.handle)
end
parameter_index = parameter_index + 1
end
end
end
local function bind_by_names(parameters)
local parameter_names = stmt.paranames
local mapping = stmt.mapping
for _, handle in ipairs(stmt.handles) do
for index = 1, api.bind_parameter_count(handle) do
local status = api.bind(handle, index, parameters[parameter_names[mapping[index]]])
if is_error(status) then
return nil, errmsg(stmt.db.handle)
end
end
end
end
check_stmt(stmt)
if stmt.parameter_count == 0 then error("stmt:bind: statement contains no parameters.") end
if type(arg[1]) == "table" and arg.n == 1 and stmt.mapping and stmt.paranames then
bind_by_names(arg[1])
else
if arg.n < stmt.parameter_count then error("stmt:bind: to few parameters.") end
if arg.n > stmt.parameter_count then error("stmt:bind: to many parameters.") end
if stmt.mapping then
bind_with_mapping(arg)
else
bind_without_mapping(arg)
end
end
return stmt
end
function stmt_class.reset(stmt)
check_stmt(stmt)
for _, handle in ipairs(stmt.handles) do
api.reset(handle)
end
return stmt
end
function stmt_class.close(stmt)
check_stmt(stmt)
for _, handle in ipairs(stmt.handles) do
api.finalize(handle)
end
unregister(stmt.db.stmts, stmt.reg_id)
stmt.db = nil
stmt.handles = nil
stmt.mapping = nil
stmt.__gc = nil
return stmt
end
local no_parameter_names = { }
function stmt_class.parameter_names(stmt)
check_stmt(stmt)
if not stmt.paranames then
return no_parameter_names
else
return stmt.paranames
end
end
local function stmt_column_info(stmt, info_func)
local handle = check_single_stmt(stmt)
local info = { }
for index = 1, api.column_count(handle) do
table.insert(info, info_func(handle, index-1) )
end
return info
end
function stmt_class.column_names(stmt)
return stmt_column_info(stmt, api.column_name)
end
function stmt_class.column_decltypes(stmt)
return stmt_column_info(stmt, api.column_decltype)
end
function stmt_class.column_count(stmt)
local handle = check_single_stmt(stmt)
return api.column_count(handle)
end
function stmt_class.exec(stmt)
check_stmt(stmt)
stmt:reset()
for _, handle in ipairs(stmt.handles) do
while true do
local status = api.step(handle)
if is_error(status) then
local errmsg = errmsg(stmt.db.handle)
api.reset(handle)
return nil, errmsg
end
if is_done(status) then
break
end
end
api.reset(handle)
end
return stmt
end
local function stmt_rows(stmt, get_row_func, tab, autoclose)
local handle = check_single_stmt(stmt)
api.reset(handle)
local function check_autoclose()
if autoclose == true then
stmt:close()
else
api.reset(handle)
end
end
local iterator = function()
local status = api.step(handle)
if is_error(status) then
local errmsg = errmsg(stmt.db.handle)
check_autoclose()
return nil, errmsg
end
if is_row(status) then
return get_row_func(handle, tab)
end
if is_done(status) then
check_autoclose()
return nil
end
return nil, "stmt:rows: Internal error!"
end
return iterator
end
function stmt_class.irows(stmt, tab, autoclose)
return stmt_rows(stmt, api.irow, tab, autoclose)
end
function stmt_class.rows(stmt, tab, autoclose)
return stmt_rows(stmt, api.arow, tab, autoclose)
end
function stmt_class.cols(stmt, autoclose)
return stmt_rows(stmt, api.drow, nil, autoclose)
end
local function first_row(stmt, get_row_func, tab, autoclose)
local handle = check_single_stmt(stmt)
api.reset(handle)
local function check_autoclose()
if autoclose == true then
stmt:close()
else
api.reset(handle)
end
end
local status = api.step(handle)
if is_error(status) then
local errmsg = errmsg(stmt.db.handle)
check_autoclose()
return nil, errmsg
end
if is_row(status) then
local row = get_row_func(handle, tab)
check_autoclose()
return row
end
if is_done(status) then
check_autoclose()
return nil, "No row returned."
end
return nil, "stmt:first_row: Internal error!"
end
function stmt_class.first_irow(stmt, tab, autoclose)
return first_row(stmt, api.irow, tab, autoclose)
end
function stmt_class.first_row(stmt, tab, autoclose)
return first_row(stmt, api.arow, tab, autoclose)
end
function stmt_class.first_cols(stmt, autoclose)
local count = api.column_count(stmt.handles[1])
local row, errmsg = first_row(stmt, api.irow, nil, autoclose)
if errmsg then
return nil, errmsg
else
row.n = count -- Make lua-5.0.2 unpack() happy
return unpack(row, 1, count) -- lua-5.1 style / lua-5.0.2 ignores additional arguments
end
end
......@@ -22,6 +22,7 @@ solution "TEngine"
"src/luasocket",
"src/fov",
"src/libtcod_import",
"src/sqlite3",
"src/physfs",
"src/physfs/zlib123",
"/usr/include/SDL",
......@@ -64,7 +65,7 @@ project "TEngine"
language "C"
targetname "t-engine"
files { "src/*.c", }
links { "physfs", "lua".._OPTIONS.lua, "fov", "luasocket", "luaprofiler", "lualanes", "lpeg", "tcodimport" }
links { "physfs", "lua".._OPTIONS.lua, "fov", "luasocket", "luaprofiler", "lualanes", "lpeg", "tcodimport", "sqlite_amalgation" }
defines { "_DEFAULT_VIDEOMODE_FLAGS_='SDL_HWSURFACE|SDL_DOUBLEBUF'" }
defines { [[TENGINE_HOME_PATH='".t-engine"']] }
......@@ -205,3 +206,10 @@ project "tcodimport"
targetname "tcodimport"
files { "src/libtcod_import/*.c", }
project "sqlite_amalgation"
kind "StaticLib"
language "C"
targetname "sqlite_amalgation"
files { "src/sqlite3/*.c", }
......@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include "sqlite3.h"
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
......@@ -37,6 +38,7 @@
#include "core_lua.h"
#include "getself.h"
#include "music.h"
#include "sqlite_physfs_vfs.h"
#include "main.h"
#define WIDTH 800
......@@ -67,6 +69,7 @@ int luaopen_sound(lua_State *L);
int luaopen_lanes(lua_State *L);
int luaopen_shaders(lua_State *L);
int luaopen_noise(lua_State *L);
int luaopen_sqlite3(lua_State * L);
static int traceback (lua_State *L) {
lua_Debug ar;
......@@ -495,6 +498,7 @@ void boot_lua(int state, bool rebooting, int argc, char *argv[])
luaopen_sound(L);
luaopen_noise(L);
luaopen_shaders(L);
luaopen_sqlite3(L);
// Make the uids repository
lua_newtable(L);
......@@ -560,6 +564,9 @@ int main(int argc, char *argv[])
// RNG init
init_gen_rand(time(NULL));
// Tell sqlite3 to use physfs to open files
sqlite3_register_physfs_vfs(TRUE);
boot_lua(1, FALSE, argc, argv);
// initialize engine and set up resolution and depth
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
Copyright (c) 2008 Giel van Schijndel
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
/*
Modified by Nicolas Casalini for inclusion in T-Engine4.
*/
#include "types.h"
#include "sqlite_physfs_vfs.h"
#include "sqlite3.h"
#include "physfs.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define WZ_DECL_UNUSED
#ifndef false
#define false FALSE
#endif
#ifndef true
#define true TRUE
#endif
/* safe strcpy */
size_t my_strlcpy(char* dest, char* src, size_t len)
{
size_t idx = 0;
size_t srclen = strlen(src);
char* srcaddr = src;
char* dstaddr = dest;
/* always leave room for the terminating null */
if (srclen >= len)
srclen = len - 1;
while (*srcaddr && idx < srclen)
{
*dstaddr++ = *srcaddr++;
idx++;
}
*dstaddr = '\0';
return idx;
}
typedef struct physfs_sqlite3_file physfs_sqlite3_file;
/** "Subclassed" sqlite3_file for this PhysicsFS VFS.
*/
struct physfs_sqlite3_file
{
sqlite3_file SQLite3_file;
PHYSFS_file* file;
bool NOOP; /**< Flag which indicates whether all write operations should be no-ops (and reads should fail) */
};
/** Closes the given file and deallocates any associated resources.
* \param f the physfs_sqlite3_file to close
* \return SQLITE_OK on success.
*/
static int xClose(sqlite3_file* f)
{
physfs_sqlite3_file * const file = (physfs_sqlite3_file * const)f;
assert(&file->SQLite3_file == f);
if (file->NOOP)
return SQLITE_OK;
if (file->file)
PHYSFS_close(file->file);
return SQLITE_OK;
}
/** Reads bytes from the file into the given output buffer.
* \param f the physfs_sqlite3_file to read from.
* \param[out] dst the memory buffer to write to.
* \param iAmt the amount of bytes to read from the file.
* \param iOfst the (absolute) offset into the file to start reading from.
* \return SQLITE_OK on success, SQLITE_IOERR_SHORT_READ if we couldn't read
* the full amount of \c iAmt bytes because EOF was reached, or
* SQLITE_IOERR_READ for any other error.
*/
static int xRead(sqlite3_file* f, void* dst, int iAmt, sqlite3_int64 iOfst)
{
PHYSFS_sint64 nRead;
physfs_sqlite3_file * const file = (physfs_sqlite3_file * const)f;
assert(&file->SQLite3_file == f);
if (file->NOOP)
return SQLITE_IOERR;
/* Seek to the given offset */
if (!PHYSFS_seek(file->file, iOfst))
return SQLITE_IOERR_READ;
nRead = PHYSFS_read(file->file, dst, 1, iAmt);
if (nRead < 0)
return SQLITE_IOERR_READ;
else if (nRead < iAmt)
return SQLITE_IOERR_SHORT_READ;
else
return SQLITE_OK;
}
/** Dummy write implementation (doesn't do anything at all).
* \return SQLITE_IOERR_WRITE
*/
static int xWrite(sqlite3_file* f, WZ_DECL_UNUSED const void* src, WZ_DECL_UNUSED int iAmt, WZ_DECL_UNUSED sqlite3_int64 iOfst)
{
physfs_sqlite3_file * const file = (physfs_sqlite3_file * const)f;
assert(&file->SQLite3_file == f);
if (file->NOOP)
return SQLITE_OK;
return SQLITE_IOERR_WRITE;
}
/** Dummy truncate implementation (doesn't do anything at all).
* \return SQLITE_IOERR_TRUNCATE
*/
static int xTruncate(sqlite3_file* f, WZ_DECL_UNUSED sqlite3_int64 size)
{
physfs_sqlite3_file * const file = (physfs_sqlite3_file * const)f;
assert(&file->SQLite3_file == f);
if (file->NOOP)
return SQLITE_OK;
return SQLITE_IOERR_TRUNCATE;
}
/** Dummy sync implementation (doesn't do anything at all).
* \return SQLITE_IOERR_FSYNC
*/
static int xSync(sqlite3_file* f, WZ_DECL_UNUSED int flags)
{
physfs_sqlite3_file * const file = (physfs_sqlite3_file * const)f;
assert(&file->SQLite3_file == f);
if (file->NOOP)
return SQLITE_OK;
return SQLITE_IOERR_FSYNC;
}
/** Determines the size of the given file.
* \param f the physfs_sqlite3_file to determine the size of.
* \param[out] pSize to write the size of this file to.
* \return
*/
static int xFileSize(sqlite3_file* f, sqlite3_int64* pSize)
{
PHYSFS_sint64 size;
physfs_sqlite3_file * const file = (physfs_sqlite3_file * const)f;
assert(&file->SQLite3_file == f);
if (file->NOOP)
return SQLITE_IOERR;
size = PHYSFS_fileLength(file->file);
if (size < 0)
return SQLITE_IOERR_FSTAT;
*pSize = size;
return SQLITE_OK;
}
static int xLock(WZ_DECL_UNUSED sqlite3_file* f, int level)
{
switch (level)
{
case SQLITE_LOCK_NONE:
case SQLITE_LOCK_SHARED:
return SQLITE_OK;
case SQLITE_LOCK_RESERVED:
case SQLITE_LOCK_PENDING:
case SQLITE_LOCK_EXCLUSIVE:
default:
return SQLITE_IOERR_RDLOCK;
}
}
static int xUnlock(WZ_DECL_UNUSED sqlite3_file* f, WZ_DECL_UNUSED int level)
{
return SQLITE_OK;
}
static int xCheckReservedLock(WZ_DECL_UNUSED sqlite3_file* f, int *pResOut)
{
*pResOut = false;
return SQLITE_OK;
}
static int xFileControl(WZ_DECL_UNUSED sqlite3_file* f, WZ_DECL_UNUSED int op, WZ_DECL_UNUSED void *pArg)
{
return 0;
}
static int xSectorSize(WZ_DECL_UNUSED sqlite3_file* f)
{
return 512;
}
static int xDeviceCharacteristics(WZ_DECL_UNUSED sqlite3_file* f)
{
return 0;
}
static const sqlite3_io_methods physfs_sqlite3_io_methods =
{
1, /**< iVersion */
xClose, /**< Closes the physfs_sqlite3_file */
xRead, /**< Reads from the physfs_sqlite3_file */
xWrite, /**< Intended to write from the physfs_sqlite3_file */
xTruncate, /**< Intended to truncate the physfs_sqlite3_file */
xSync, /**< Intended to flush data out of OS cache into non-volatile memory */
xFileSize, /**< Deterimines the physfs_sqlite3_file's size */
xLock, /**< Acquires a lock on the physfs_sqlite3_file */
xUnlock, /**< Releases a lock on the physfs_sqlite3_file */
xCheckReservedLock, /**< Checks whether a "reserved" (or higher) level lock is acquired on this physfs_sqlite3_file */
xFileControl, /**< Intended to execute the given opcode */
xSectorSize, /**< Determines the size of sectors for the underlying filesystem */
xDeviceCharacteristics, /**< Returns a bitmask describing the underlying filesystem's characteristics */
};
static int xOpen(WZ_DECL_UNUSED sqlite3_vfs* pVfs, const char* zName, sqlite3_file* f, int flags, int* pOutFlags)
{
physfs_sqlite3_file * const file = (physfs_sqlite3_file * const)f;
assert(&file->SQLite3_file == f);
/* Assign the "overloaded" I/O functions to this file object */
file->SQLite3_file.pMethods = &physfs_sqlite3_io_methods;
/* We don't support journals in this implementation */
if (flags & SQLITE_OPEN_MAIN_JOURNAL
|| flags & SQLITE_OPEN_TEMP_JOURNAL
|| flags & SQLITE_OPEN_MASTER_JOURNAL
|| flags & SQLITE_OPEN_SUBJOURNAL)
{
file->file = 0;
file->NOOP = true;
}
else /* if (flags & SQLITE_OPEN_MAIN_DB
|| flags & SQLITE_OPEN_TEMP_DB
|| flags & SQLITE_OPEN_TRANSIENT_DB) */
{
/* SQLite requires that we open a temporary file if zName is
* NULL, but we currently do not provide an implementation for
* that, so bail out.
*/
if (zName == NULL)
return SQLITE_IOERR;
file->file = PHYSFS_openRead(zName);
file->NOOP = false;
if (!file->file)
return SQLITE_IOERR;
}
*pOutFlags = SQLITE_OPEN_READONLY;
return SQLITE_OK;
}
static int xDelete(WZ_DECL_UNUSED sqlite3_vfs* pVfs, const char* zName, WZ_DECL_UNUSED int syncDir)
{
if (PHYSFS_delete(zName))
return SQLITE_OK;
else
return SQLITE_IOERR_DELETE;
}
static int xAccess(WZ_DECL_UNUSED sqlite3_vfs* pVfs, const char* zName, int flags, int *pResOut)
{
switch (flags)
{
case SQLITE_ACCESS_EXISTS:
*pResOut = PHYSFS_exists(zName);
break;
case SQLITE_ACCESS_READ:
{
PHYSFS_file* f = PHYSFS_openRead(zName);
*pResOut = (f != NULL);
PHYSFS_close(f);
break;
}
case SQLITE_ACCESS_READWRITE:
default:
*pResOut = 0;
}
return SQLITE_OK;
}
/** \return non-zero when no truncation occurred, zero otherwise.
*/
static int xFullPathname(WZ_DECL_UNUSED sqlite3_vfs* pVfs, const char* zName, int nOut, char* zOut)
{
/* nOut isn't ever negative, because its a buffer length, and the
* comparison below would cause a signed/unsigned comparison warning.
*/
const unsigned int _nOut = nOut;
assert(nOut > 0);
return (my_strlcpy(zOut, zName, _nOut) < _nOut) ? SQLITE_OK : SQLITE_IOERR;
}
static void* xDlOpen(WZ_DECL_UNUSED sqlite3_vfs* pVfs, WZ_DECL_UNUSED const char* zFilename)
{
return NULL;
}
static void xDlError(WZ_DECL_UNUSED sqlite3_vfs* pVfs, int nByte, char* zErrMsg)
{
my_strlcpy(zErrMsg, "DlOpen and DlSym API isn't supported for this (PhysicsFS \"physfs\") VFS.", nByte);
}
static void (*xDlSym(WZ_DECL_UNUSED sqlite3_vfs* pVfs, WZ_DECL_UNUSED void* dl, WZ_DECL_UNUSED const char* zSymbol))(void)
{
return NULL;
}
static void xDlClose(WZ_DECL_UNUSED sqlite3_vfs* pVfs, WZ_DECL_UNUSED void* dl)
{
}
static int xRandomness(WZ_DECL_UNUSED sqlite3_vfs* pVfs, WZ_DECL_UNUSED int nByte, WZ_DECL_UNUSED char* zOut)
{
return SQLITE_IOERR;
}
static int xSleep(WZ_DECL_UNUSED sqlite3_vfs* pVfs, WZ_DECL_UNUSED int microseconds)
{
return SQLITE_IOERR;
}
static int xCurrentTime(WZ_DECL_UNUSED sqlite3_vfs* pVfs, WZ_DECL_UNUSED double* curTime)
{
return SQLITE_IOERR;
}
static int xGetLastError(WZ_DECL_UNUSED sqlite3_vfs* pVfs, int nBuf, char* zBuf)
{
/* nBuf isn't ever negative, because its a buffer length, and the
* comparison below would cause a signed/unsigned comparison warning.
*/
const unsigned int _nBuf = nBuf;
assert(nBuf > 0);
return (my_strlcpy(zBuf, PHYSFS_getLastError(), _nBuf) < _nBuf) ? SQLITE_OK : SQLITE_IOERR;
}
static sqlite3_vfs physfs_sqlite3_vfs =
{
1, /**< Structure version number */
sizeof(physfs_sqlite3_vfs), /**< Size of subclassed sqlite3_file */
256, /**< Maximum file pathname length */
0, /**< Next registered VFS (managed by SQLite) */
"physfs", /**< Name of this virtual file system */
0, /**< Pointer to application-specific data */
xOpen,
xDelete,
xAccess,
xFullPathname,
xDlOpen,
xDlError,
xDlSym,
xDlClose,
xRandomness,
xSleep,
xCurrentTime,
xGetLastError,
};
void sqlite3_register_physfs_vfs(int makeDefault)
{
sqlite3_vfs_register(&physfs_sqlite3_vfs, makeDefault);
}
void sqlite3_unregister_physfs_vfs()
{
sqlite3_vfs_unregister(&physfs_sqlite3_vfs);
}
/*
Copyright (c) 2008 Giel van Schijndel
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef __INCLUDED_PHYSICSFS_SQLITE3_VFS_H__
#define __INCLUDED_PHYSICSFS_SQLITE3_VFS_H__
/** Registers the PhysicsFS implementation of SQLite's VFS with name "physfs"
* \param makeDefault specifies whether this VFS should be made
* the default VFS, non-zero to make it the default,
* zero to leave whatever is currently the default as the
* default.
* \note If you want to make this VFS implementation the default if you didn't
* made it the default the first time, or registered another VFS to be
* the default as well, then you can simply call this function again with
* \c makeDefault non-zero.
*/
extern void sqlite3_register_physfs_vfs(int makeDefault);
/** Unregisters the PhysicsFS implementation of SQLite's VFS from
* SQLite's VFS-list.
* \note This will _not_ affect currently opened database connections,
* only new ones.
*/
extern void sqlite3_unregister_physfs_vfs(void);
#endif // __INCLUDED_PHYSICSFS_SQLITE3_VFS_H__
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment