mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-02-23 14:42:33 +01:00
byuu says: First 32 instructions implemented in the TLCS900H disassembler. Only 992 to go! I removed the use of anonymous namespaces in nall. It was something I rarely used, because it rarely did what I wanted. I updated all nested namespaces to use C++17-style namespace Foo::Bar {} syntax instead of classic C++-style namespace Foo { namespace Bar {}}. I updated ruby::Video::acquire() to return a struct, so we can use C++17 structured bindings. Long term, I want to get away from all functions that take references for output only. Even though C++ botched structured bindings by not allowing you to bind to existing variables, it's even worse to have function calls that take arguments by reference and then write to them. From the caller side, you can't tell the value is being written, nor that the value passed in doesn't matter, which is terrible.
204 lines
6.5 KiB
C++
204 lines
6.5 KiB
C++
#pragma once
|
|
|
|
/* SQLite3 C++ RAII wrapper for nall
|
|
*
|
|
* Note on code below: it is safe (no-op) to call sqlite3_* functions on null sqlite3 objects
|
|
*/
|
|
|
|
#include <sqlite3.h>
|
|
|
|
#include <nall/stdint.hpp>
|
|
#include <nall/string.hpp>
|
|
|
|
namespace nall::Database {
|
|
|
|
struct SQLite3 {
|
|
struct Statement {
|
|
Statement(const Statement& source) = delete;
|
|
auto operator=(const Statement& source) -> Statement& = delete;
|
|
|
|
Statement(sqlite3_stmt* statement) : _statement(statement) {}
|
|
Statement(Statement&& source) { operator=(move(source)); }
|
|
|
|
auto operator=(Statement&& source) -> Statement& {
|
|
_statement = source._statement;
|
|
_response = source._response;
|
|
_output = source._output;
|
|
source._statement = nullptr;
|
|
source._response = SQLITE_OK;
|
|
source._output = 0;
|
|
return *this;
|
|
}
|
|
|
|
explicit operator bool() {
|
|
return sqlite3_data_count(statement());
|
|
}
|
|
|
|
auto columns() -> unsigned {
|
|
return sqlite3_column_count(statement());
|
|
}
|
|
|
|
auto integer(unsigned column) -> int64_t {
|
|
return sqlite3_column_int64(statement(), column);
|
|
}
|
|
|
|
auto natural(unsigned column) -> uint64_t {
|
|
return sqlite3_column_int64(statement(), column);
|
|
}
|
|
|
|
auto real(unsigned column) -> double {
|
|
return sqlite3_column_double(statement(), column);
|
|
}
|
|
|
|
auto text(unsigned column) -> string {
|
|
string result;
|
|
if(auto text = sqlite3_column_text(statement(), column)) {
|
|
result.resize(sqlite3_column_bytes(statement(), column));
|
|
memory::copy(result.get(), text, result.size());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto data(unsigned column) -> vector<uint8_t> {
|
|
vector<uint8_t> result;
|
|
if(auto data = sqlite3_column_blob(statement(), column)) {
|
|
result.resize(sqlite3_column_bytes(statement(), column));
|
|
memory::copy(result.data(), data, result.size());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto integer() -> int64_t { return integer(_output++); }
|
|
auto natural() -> uint64_t { return natural(_output++); }
|
|
auto real() -> double { return real(_output++); }
|
|
auto text() -> string { return text(_output++); }
|
|
auto data() -> vector<uint8_t> { return data(_output++); }
|
|
|
|
protected:
|
|
virtual auto statement() -> sqlite3_stmt* { return _statement; }
|
|
|
|
sqlite3_stmt* _statement = nullptr;
|
|
signed _response = SQLITE_OK;
|
|
unsigned _output = 0;
|
|
};
|
|
|
|
struct Query : Statement {
|
|
Query(const Query& source) = delete;
|
|
auto operator=(const Query& source) -> Query& = delete;
|
|
|
|
Query(sqlite3_stmt* statement) : Statement(statement) {}
|
|
Query(Query&& source) : Statement(source._statement) { operator=(move(source)); }
|
|
|
|
~Query() {
|
|
sqlite3_finalize(statement());
|
|
_statement = nullptr;
|
|
}
|
|
|
|
auto operator=(Query&& source) -> Query& {
|
|
_statement = source._statement;
|
|
_input = source._input;
|
|
source._statement = nullptr;
|
|
source._input = 0;
|
|
return *this;
|
|
}
|
|
|
|
auto& bind(unsigned column, nullptr_t) { sqlite3_bind_null(_statement, 1 + column); return *this; }
|
|
auto& bind(unsigned column, int32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
|
auto& bind(unsigned column, uint32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
|
auto& bind(unsigned column, int64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
|
auto& bind(unsigned column, uint64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
|
auto& bind(unsigned column, double value) { sqlite3_bind_double(_statement, 1 + column, value); return *this; }
|
|
auto& bind(unsigned column, const string& value) { sqlite3_bind_text(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
|
auto& bind(unsigned column, const vector<uint8_t>& value) { sqlite3_bind_blob(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
|
|
|
auto& bind(nullptr_t) { return bind(_input++, nullptr); }
|
|
auto& bind(int32_t value) { return bind(_input++, value); }
|
|
auto& bind(uint32_t value) { return bind(_input++, value); }
|
|
auto& bind(int64_t value) { return bind(_input++, value); }
|
|
auto& bind(uint64_t value) { return bind(_input++, value); }
|
|
auto& bind(double value) { return bind(_input++, value); }
|
|
auto& bind(const string& value) { return bind(_input++, value); }
|
|
auto& bind(const vector<uint8_t>& value) { return bind(_input++, value); }
|
|
|
|
auto step() -> bool {
|
|
_stepped = true;
|
|
return sqlite3_step(_statement) == SQLITE_ROW;
|
|
}
|
|
|
|
struct Iterator {
|
|
Iterator(Query& query, bool finished) : query(query), finished(finished) {}
|
|
auto operator*() -> Statement { return query._statement; }
|
|
auto operator!=(const Iterator& source) const -> bool { return finished != source.finished; }
|
|
auto operator++() -> Iterator& { finished = !query.step(); return *this; }
|
|
|
|
protected:
|
|
Query& query;
|
|
bool finished = false;
|
|
};
|
|
|
|
auto begin() -> Iterator { return Iterator(*this, !step()); }
|
|
auto end() -> Iterator { return Iterator(*this, true); }
|
|
|
|
private:
|
|
auto statement() -> sqlite3_stmt* override {
|
|
if(!_stepped) step();
|
|
return _statement;
|
|
}
|
|
|
|
unsigned _input = 0;
|
|
bool _stepped = false;
|
|
};
|
|
|
|
SQLite3() = default;
|
|
SQLite3(const string& filename) { open(filename); }
|
|
~SQLite3() { close(); }
|
|
|
|
explicit operator bool() const { return _database; }
|
|
|
|
auto open(const string& filename) -> bool {
|
|
close();
|
|
sqlite3_open(filename, &_database);
|
|
return _database;
|
|
}
|
|
|
|
auto close() -> void {
|
|
sqlite3_close(_database);
|
|
_database = nullptr;
|
|
}
|
|
|
|
template<typename... P> auto execute(const string& statement, P&&... p) -> Query {
|
|
if(!_database) return {nullptr};
|
|
|
|
sqlite3_stmt* _statement = nullptr;
|
|
sqlite3_prepare_v2(_database, statement.data(), statement.size(), &_statement, nullptr);
|
|
if(!_statement) {
|
|
if(_debug) print("[sqlite3_prepare_v2] ", sqlite3_errmsg(_database), "\n");
|
|
return {nullptr};
|
|
}
|
|
|
|
Query query{_statement};
|
|
bind(query, forward<P>(p)...);
|
|
return query;
|
|
}
|
|
|
|
auto lastInsertID() const -> uint64_t {
|
|
return _database ? sqlite3_last_insert_rowid(_database) : 0;
|
|
}
|
|
|
|
auto setDebug(bool debug = true) -> void {
|
|
_debug = debug;
|
|
}
|
|
|
|
protected:
|
|
auto bind(Query&) -> void {}
|
|
template<typename T, typename... P> auto bind(Query& query, const T& value, P&&... p) -> void {
|
|
query.bind(value);
|
|
bind(query, forward<P>(p)...);
|
|
}
|
|
|
|
bool _debug = false;
|
|
sqlite3* _database = nullptr;
|
|
};
|
|
|
|
}
|