mirror of
https://github.com/joel16/VitaShell.git
synced 2024-11-23 11:49:40 +00:00
Merge pull request #314 from VitaSmith/license_db
Add license database refresh
This commit is contained in:
commit
72c0d5f3de
4
.editorconfig
Normal file
4
.editorconfig
Normal file
@ -0,0 +1,4 @@
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
@ -83,6 +83,8 @@ add_executable(VitaShell
|
||||
text.c
|
||||
hex.c
|
||||
sfo.c
|
||||
rif.c
|
||||
sqlite3.c
|
||||
coredump.c
|
||||
settings.c
|
||||
property_dialog.c
|
||||
|
BIN
db/licenses.db
BIN
db/licenses.db
Binary file not shown.
@ -1,18 +0,0 @@
|
||||
This directory contains an example of the type of database one can use to
|
||||
store personal licenses, so that VitaShell can automatically restore them
|
||||
when reinstalling content from PKG data.
|
||||
|
||||
As opposed to this sample, which does not contain any usable data, your
|
||||
CONTENT_ID columns should contain valid content id for the PS Vita app,
|
||||
game or DLC you own, and the RIF columns should contain a 512-byte .rif
|
||||
blob that includes your AID, a CONTENT_ID that matches the one from the
|
||||
first column as well as your personal license data.
|
||||
|
||||
To edit this database manually, you can use the Windows/Linux/MacOS
|
||||
compatible DB Browser for SQLite (http://sqlitebrowser.org/) to drag and
|
||||
drop your .rif files and edit the relevant CONTENT_ID fields.
|
||||
|
||||
Or you can wait until someone writes an application that automates that
|
||||
process or you... ;)
|
||||
|
||||
This database should be copied to ux0:license/
|
13
init.c
13
init.c
@ -22,6 +22,7 @@
|
||||
#include "package_installer.h"
|
||||
#include "utils.h"
|
||||
#include "qr.h"
|
||||
#include "rif.h"
|
||||
|
||||
#include "audio/vita_audio.h"
|
||||
|
||||
@ -253,12 +254,11 @@ static void finishVita2dLib() {
|
||||
}
|
||||
|
||||
static int initSQLite() {
|
||||
SceSqliteMallocMethods mf = {
|
||||
(void* (*) (int)) malloc,
|
||||
(void* (*) (void*, int)) realloc,
|
||||
free
|
||||
};
|
||||
return sceSqliteConfigMallocMethods(&mf);
|
||||
return sqlite_init();
|
||||
}
|
||||
|
||||
static int finishSQLite() {
|
||||
return sqlite_exit();
|
||||
}
|
||||
|
||||
static void initNet() {
|
||||
@ -394,6 +394,7 @@ void initVitaShell() {
|
||||
|
||||
void finishVitaShell() {
|
||||
// Finish
|
||||
finishSQLite();
|
||||
finishNet();
|
||||
finishSceAppUtil();
|
||||
finishVita2dLib();
|
||||
|
@ -44,11 +44,11 @@ static char *lang[] ={
|
||||
"turkish"
|
||||
};
|
||||
|
||||
char *language_container[LANGUAGE_CONTRAINER_SIZE];
|
||||
char *language_container[LANGUAGE_CONTAINER_SIZE];
|
||||
|
||||
void freeLanguageContainer() {
|
||||
int i;
|
||||
for (i = 0; i < LANGUAGE_CONTRAINER_SIZE; i++) {
|
||||
for (i = 0; i < LANGUAGE_CONTAINER_SIZE; i++) {
|
||||
if (language_container[i]) {
|
||||
free(language_container[i]);
|
||||
language_container[i] = NULL;
|
||||
@ -101,6 +101,7 @@ void loadLanguage(int id) {
|
||||
|
||||
// Context menu strings
|
||||
LANGUAGE_ENTRY(REFRESH_LIVEAREA),
|
||||
LANGUAGE_ENTRY(REFRESH_LICENSE_DB),
|
||||
LANGUAGE_ENTRY(MOUNT_UMA0),
|
||||
LANGUAGE_ENTRY(MOUNT_IMC0),
|
||||
LANGUAGE_ENTRY(MOUNT_USB_UX0),
|
||||
@ -161,6 +162,7 @@ void loadLanguage(int id) {
|
||||
LANGUAGE_ENTRY(COPIED_FILE),
|
||||
LANGUAGE_ENTRY(COPIED_FOLDER),
|
||||
LANGUAGE_ENTRY(COPIED_FILES_FOLDERS),
|
||||
LANGUAGE_ENTRY(IMPORTED_LICENSES),
|
||||
|
||||
// Dialog questions
|
||||
LANGUAGE_ENTRY(DELETE_FILE_QUESTION),
|
||||
@ -185,6 +187,7 @@ void loadLanguage(int id) {
|
||||
LANGUAGE_ENTRY(HASH_FILE_QUESTION),
|
||||
LANGUAGE_ENTRY(SAVE_MODIFICATIONS),
|
||||
LANGUAGE_ENTRY(REFRESH_LIVEAREA_QUESTION),
|
||||
LANGUAGE_ENTRY(REFRESH_LICENSE_DB_QUESTION),
|
||||
|
||||
// HENkaku settings strings
|
||||
LANGUAGE_ENTRY(HENKAKU_SETTINGS),
|
||||
|
@ -60,6 +60,7 @@ enum LanguageContainer {
|
||||
|
||||
// Context menu strings
|
||||
REFRESH_LIVEAREA,
|
||||
REFRESH_LICENSE_DB,
|
||||
MOUNT_UMA0,
|
||||
MOUNT_IMC0,
|
||||
MOUNT_USB_UX0,
|
||||
@ -120,6 +121,7 @@ enum LanguageContainer {
|
||||
COPIED_FILE,
|
||||
COPIED_FOLDER,
|
||||
COPIED_FILES_FOLDERS,
|
||||
IMPORTED_LICENSES,
|
||||
|
||||
// Dialog questions
|
||||
DELETE_FILE_QUESTION,
|
||||
@ -144,6 +146,7 @@ enum LanguageContainer {
|
||||
HASH_FILE_QUESTION,
|
||||
SAVE_MODIFICATIONS,
|
||||
REFRESH_LIVEAREA_QUESTION,
|
||||
REFRESH_LICENSE_DB_QUESTION,
|
||||
|
||||
// HENkaku settings strings
|
||||
HENKAKU_SETTINGS,
|
||||
@ -207,10 +210,10 @@ enum LanguageContainer {
|
||||
UPDATE_QUESTION,
|
||||
ARCHIVE_NAME,
|
||||
COMPRESSION_LEVEL,
|
||||
LANGUAGE_CONTRAINER_SIZE,
|
||||
LANGUAGE_CONTAINER_SIZE,
|
||||
};
|
||||
|
||||
extern char *language_container[LANGUAGE_CONTRAINER_SIZE];
|
||||
extern char *language_container[LANGUAGE_CONTAINER_SIZE];
|
||||
|
||||
void freeLanguageContainer();
|
||||
void loadLanguage(int id);
|
||||
|
16
main.c
16
main.c
@ -660,6 +660,22 @@ static int dialogSteps() {
|
||||
break;
|
||||
}
|
||||
|
||||
case DIALOG_STEP_REFRESH_LICENSE_DB_QUESTION:
|
||||
{
|
||||
if (msg_result == MESSAGE_DIALOG_RESULT_YES) {
|
||||
initMessageDialog(MESSAGE_DIALOG_PROGRESS_BAR, language_container[REFRESHING]);
|
||||
setDialogStep(DIALOG_STEP_REFRESHING);
|
||||
|
||||
SceUID thid = sceKernelCreateThread("license_thread", (SceKernelThreadEntry)license_thread, 0x40, 0x100000, 0, 0, NULL);
|
||||
if (thid >= 0)
|
||||
sceKernelStartThread(thid, 0, NULL);
|
||||
} else if (msg_result == MESSAGE_DIALOG_RESULT_NO) {
|
||||
setDialogStep(DIALOG_STEP_NONE);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DIALOG_STEP_USB_ATTACH_WAIT:
|
||||
{
|
||||
if (msg_result == MESSAGE_DIALOG_RESULT_RUNNING) {
|
||||
|
3
main.h
3
main.h
@ -189,8 +189,9 @@ enum DialogSteps {
|
||||
DIALOG_STEP_SYSTEM,
|
||||
|
||||
DIALOG_STEP_REFRESH_LIVEAREA_QUESTION,
|
||||
DIALOG_STEP_REFRESH_LICENSE_DB_QUESTION,
|
||||
DIALOG_STEP_REFRESHING,
|
||||
|
||||
|
||||
DIALOG_STEP_USB_ATTACH_WAIT,
|
||||
|
||||
DIALOG_STEP_FTP_WAIT,
|
||||
|
@ -33,6 +33,7 @@ char pfs_mount_point[MAX_MOUNT_POINT_LENGTH];
|
||||
|
||||
enum MenuHomeEntrys {
|
||||
MENU_HOME_ENTRY_REFRESH_LIVEAREA,
|
||||
MENU_HOME_ENTRY_REFRESH_LICENSE_DB,
|
||||
MENU_HOME_ENTRY_MOUNT_UMA0,
|
||||
MENU_HOME_ENTRY_MOUNT_IMC0,
|
||||
MENU_HOME_ENTRY_MOUNT_USB_UX0,
|
||||
@ -40,11 +41,12 @@ enum MenuHomeEntrys {
|
||||
};
|
||||
|
||||
MenuEntry menu_home_entries[] = {
|
||||
{ REFRESH_LIVEAREA, 0, 0, CTX_INVISIBLE },
|
||||
{ MOUNT_UMA0, 1, 0, CTX_INVISIBLE },
|
||||
{ MOUNT_IMC0, 2, 0, CTX_INVISIBLE },
|
||||
{ MOUNT_USB_UX0, 4, 0, CTX_INVISIBLE },
|
||||
{ UMOUNT_USB_UX0, 5, 0, CTX_INVISIBLE },
|
||||
{ REFRESH_LIVEAREA, 0, 0, CTX_INVISIBLE },
|
||||
{ REFRESH_LICENSE_DB, 1, 0, CTX_INVISIBLE },
|
||||
{ MOUNT_UMA0, 2, 0, CTX_INVISIBLE },
|
||||
{ MOUNT_IMC0, 3, 0, CTX_INVISIBLE },
|
||||
{ MOUNT_USB_UX0, 5, 0, CTX_INVISIBLE },
|
||||
{ UMOUNT_USB_UX0, 6, 0, CTX_INVISIBLE },
|
||||
};
|
||||
|
||||
#define N_MENU_HOME_ENTRIES (sizeof(menu_home_entries) / sizeof(MenuEntry))
|
||||
@ -465,7 +467,19 @@ static int contextMenuHomeEnterCallback(int sel, void *context) {
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case MENU_HOME_ENTRY_REFRESH_LICENSE_DB:
|
||||
{
|
||||
if (is_safe_mode) {
|
||||
infoDialog(language_container[EXTENDED_PERMISSIONS_REQUIRED]);
|
||||
} else {
|
||||
initMessageDialog(SCE_MSG_DIALOG_BUTTON_TYPE_YESNO, language_container[REFRESH_LICENSE_DB_QUESTION]);
|
||||
setDialogStep(DIALOG_STEP_REFRESH_LICENSE_DB_QUESTION);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_HOME_ENTRY_MOUNT_UMA0:
|
||||
{
|
||||
if (is_safe_mode) {
|
||||
|
167
refresh.c
167
refresh.c
@ -27,16 +27,13 @@
|
||||
#include "message_dialog.h"
|
||||
#include "language.h"
|
||||
#include "utils.h"
|
||||
#include "sqlite3.h"
|
||||
#include "rif.h"
|
||||
|
||||
// Note: The promotion process is *VERY* sensitive to the directories used below
|
||||
// Don't change them unless you know what you are doing!
|
||||
#define APP_TEMP "ux0:temp/app"
|
||||
#define DLC_TEMP "ux0:temp/addcont"
|
||||
#define DB_PATH "ux0:/license/licenses.db"
|
||||
|
||||
#define RIF_SIZE 512
|
||||
#define MAX_QUERY_LENGTH 128
|
||||
#define MAX_DLC_PER_TITLE 1024
|
||||
|
||||
int isCustomHomebrew(const char* path)
|
||||
@ -53,39 +50,6 @@ int isCustomHomebrew(const char* path)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Query a rif from an sqlite database. Returned data must be freed by the caller.
|
||||
uint8_t* query_rif(const char* db_path, const char* content_id)
|
||||
{
|
||||
int rc, rif_size = 0;
|
||||
sqlite3 *db = NULL;
|
||||
sqlite3_stmt *stmt;
|
||||
char query[MAX_QUERY_LENGTH];
|
||||
uint8_t *rif = NULL;
|
||||
|
||||
rc = sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READONLY, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
return NULL;
|
||||
|
||||
snprintf(query, sizeof(query), "SELECT RIF FROM Licenses WHERE CONTENT_ID = '%s';", content_id);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
return NULL;
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc == SQLITE_ROW)
|
||||
rif_size = sqlite3_column_bytes(stmt, 0);
|
||||
if (rif_size != RIF_SIZE)
|
||||
return NULL;
|
||||
|
||||
rif = malloc(rif_size);
|
||||
if (rif != NULL)
|
||||
memcpy(rif, sqlite3_column_blob(stmt, 0), rif_size);
|
||||
|
||||
sqlite3_close(db);
|
||||
return rif;
|
||||
}
|
||||
|
||||
int refreshNeeded(const char *app_path)
|
||||
{
|
||||
char sfo_path[MAX_PATH_LENGTH];
|
||||
@ -157,7 +121,7 @@ int refreshApp(const char *app_path)
|
||||
int sfo_size = allocateReadFile(sfo_path, &sfo_buffer);
|
||||
if (sfo_size > 0) {
|
||||
getSfoString(sfo_buffer, "CONTENT_ID", contentid, sizeof(contentid));
|
||||
uint8_t* rif = query_rif(DB_PATH, contentid);
|
||||
uint8_t* rif = query_rif(LICENSE_DB, contentid);
|
||||
if (rif != NULL) {
|
||||
int fh = sceIoOpen(work_bin_path, SCE_O_WRONLY | SCE_O_CREAT, 0777);
|
||||
if (fh > 0) {
|
||||
@ -175,7 +139,8 @@ int refreshApp(const char *app_path)
|
||||
return (res < 0) ? res : 1;
|
||||
}
|
||||
|
||||
int parse_dir_with_callback(const char* path, void(*callback)(void*, const char*, const char*), void* data)
|
||||
// target_type should be either SCE_S_IFREG for files or SCE_S_IFDIR for directories
|
||||
int parse_dir_with_callback(int target_type, const char* path, void(*callback)(void*, const char*, const char*), void* data)
|
||||
{
|
||||
SceUID dfd = sceIoDopen(path);
|
||||
if (dfd >= 0) {
|
||||
@ -187,7 +152,7 @@ int parse_dir_with_callback(const char* path, void(*callback)(void*, const char*
|
||||
|
||||
res = sceIoDread(dfd, &dir);
|
||||
if (res > 0) {
|
||||
if (SCE_S_ISDIR(dir.d_stat.st_mode)) {
|
||||
if ((dir.d_stat.st_mode & SCE_S_IFMT) == target_type) {
|
||||
callback(data, path, dir.d_name);
|
||||
if (cancelHandler()) {
|
||||
closeWaitDialog();
|
||||
@ -215,6 +180,16 @@ typedef struct {
|
||||
int list_size;
|
||||
} dlc_data_t;
|
||||
|
||||
typedef struct {
|
||||
int copy_pass;
|
||||
int count;
|
||||
int processed;
|
||||
int copied;
|
||||
int cur_depth;
|
||||
int max_depth;
|
||||
uint8_t* rif;
|
||||
} license_data_t;
|
||||
|
||||
void app_callback(void* data, const char* dir, const char* subdir)
|
||||
{
|
||||
refresh_data_t *refresh_data = (refresh_data_t*)data;
|
||||
@ -269,7 +244,7 @@ void dlc_callback_outer(void* data, const char* dir, const char* subdir)
|
||||
|
||||
// Get the title's dlc subdirectories
|
||||
int len = snprintf(path, sizeof(path), "%s/%s", dir, subdir);
|
||||
parse_dir_with_callback(path, dlc_callback_inner, &dlc_data);
|
||||
parse_dir_with_callback(SCE_S_IFDIR, path, dlc_callback_inner, &dlc_data);
|
||||
|
||||
if (refresh_data->refresh_pass) {
|
||||
// For dlc, the process happens in two phases to avoid promotion errors:
|
||||
@ -315,11 +290,11 @@ int refresh_thread(SceSize args, void *argp)
|
||||
sceKernelDelayThread(DIALOG_WAIT); // Needed to see the percentage
|
||||
|
||||
// Get the app count
|
||||
if (parse_dir_with_callback("ux0:app", app_callback, &refresh_data) < 0)
|
||||
if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:app", app_callback, &refresh_data) < 0)
|
||||
goto EXIT;
|
||||
|
||||
// Get the dlc count
|
||||
if (parse_dir_with_callback("ux0:addcont", dlc_callback_outer, &refresh_data) < 0)
|
||||
if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:addcont", dlc_callback_outer, &refresh_data) < 0)
|
||||
goto EXIT;
|
||||
|
||||
// Update thread
|
||||
@ -331,11 +306,11 @@ int refresh_thread(SceSize args, void *argp)
|
||||
refresh_data.refresh_pass = 1;
|
||||
|
||||
// Refresh apps
|
||||
if (parse_dir_with_callback("ux0:app", app_callback, &refresh_data) < 0)
|
||||
if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:app", app_callback, &refresh_data) < 0)
|
||||
goto EXIT;
|
||||
|
||||
// Refresh dlc
|
||||
if (parse_dir_with_callback("ux0:addcont", dlc_callback_outer, &refresh_data) < 0)
|
||||
if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:addcont", dlc_callback_outer, &refresh_data) < 0)
|
||||
goto EXIT;
|
||||
|
||||
sceIoRmdir("ux0:temp/addcont");
|
||||
@ -358,3 +333,105 @@ EXIT:
|
||||
|
||||
return sceKernelExitDeleteThread(0);
|
||||
}
|
||||
|
||||
// Note: This is currently not optimized AT ALL.
|
||||
// Ultimately, we want to use a single transaction and avoid trying to
|
||||
// re-insert rifs that are already present.
|
||||
void license_file_callback(void* data, const char* dir, const char* file)
|
||||
{
|
||||
license_data_t *license_data = (license_data_t*)data;
|
||||
char path[MAX_PATH_LENGTH];
|
||||
|
||||
// Ignore non rif content
|
||||
if ((strlen(file) < 4) || (strcmp(&file[strlen(file) - 4], ".rif") != 0))
|
||||
return;
|
||||
if (license_data->copy_pass) {
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, file);
|
||||
SceUID fd = sceIoOpen(path, SCE_O_RDONLY, 0777);
|
||||
if (fd > 0) {
|
||||
int read = sceIoRead(fd, license_data->rif, RIF_SIZE);
|
||||
if (read == RIF_SIZE) {
|
||||
if (insert_rif(LICENSE_DB, license_data->rif) == 0)
|
||||
license_data->copied++;
|
||||
}
|
||||
sceIoClose(fd);
|
||||
}
|
||||
SetProgress(++license_data->processed, license_data->count);
|
||||
} else {
|
||||
license_data->count++;
|
||||
}
|
||||
}
|
||||
|
||||
void license_dir_callback(void* data, const char* dir, const char* subdir)
|
||||
{
|
||||
license_data_t *license_data = (license_data_t*)data;
|
||||
char path[MAX_PATH_LENGTH];
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, subdir);
|
||||
if (++license_data->cur_depth == license_data->max_depth)
|
||||
parse_dir_with_callback(SCE_S_IFREG, path, license_file_callback, data);
|
||||
else
|
||||
parse_dir_with_callback(SCE_S_IFDIR, path, license_dir_callback, data);
|
||||
license_data->cur_depth--;
|
||||
}
|
||||
|
||||
int license_thread(SceSize args, void *argp)
|
||||
{
|
||||
SceUID thid = -1;
|
||||
license_data_t license_data = { 0, 0, 0, 0, 0, 1, malloc(RIF_SIZE) };
|
||||
|
||||
if (license_data.rif == NULL)
|
||||
goto EXIT;
|
||||
|
||||
// Lock power timers
|
||||
powerLock();
|
||||
|
||||
// Set progress to 0%
|
||||
sceMsgDialogProgressBarSetValue(SCE_MSG_DIALOG_PROGRESSBAR_TARGET_BAR_DEFAULT, 0);
|
||||
sceKernelDelayThread(DIALOG_WAIT); // Needed to see the percentage
|
||||
|
||||
// NB: ux0:license access requires elevated permisions
|
||||
if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:license/app", license_dir_callback, &license_data) < 0)
|
||||
goto EXIT;
|
||||
license_data.max_depth++;
|
||||
if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:license/addcont", license_dir_callback, &license_data) < 0)
|
||||
goto EXIT;
|
||||
|
||||
// Update thread
|
||||
thid = createStartUpdateThread(license_data.count, 0);
|
||||
|
||||
// Create the DB if needed
|
||||
SceUID fd = sceIoOpen(LICENSE_DB, SCE_O_RDONLY, 0777);
|
||||
if (fd > 0) {
|
||||
sceIoClose(fd);
|
||||
} else if (create_db(LICENSE_DB, LICENSE_DB_SCHEMA) != 0) {
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
// Insert the licenses
|
||||
license_data.copy_pass = 1;
|
||||
license_data.max_depth = 1;
|
||||
if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:license/app", license_dir_callback, &license_data) < 0)
|
||||
goto EXIT;
|
||||
license_data.max_depth++;
|
||||
if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:license/addcont", license_dir_callback, &license_data) < 0)
|
||||
goto EXIT;
|
||||
|
||||
// Set progress to 100%
|
||||
sceMsgDialogProgressBarSetValue(SCE_MSG_DIALOG_PROGRESSBAR_TARGET_BAR_DEFAULT, 100);
|
||||
sceKernelDelayThread(COUNTUP_WAIT);
|
||||
|
||||
// Close
|
||||
closeWaitDialog();
|
||||
|
||||
infoDialog(language_container[IMPORTED_LICENSES], license_data.copied);
|
||||
|
||||
EXIT:
|
||||
if (thid >= 0)
|
||||
sceKernelWaitThreadEnd(thid, NULL, NULL);
|
||||
|
||||
// Unlock power timers
|
||||
powerUnlock();
|
||||
|
||||
return sceKernelExitDeleteThread(0);
|
||||
}
|
||||
|
@ -20,5 +20,6 @@
|
||||
#define __REFRESH_H__
|
||||
|
||||
int refresh_thread(SceSize args, void *argp);
|
||||
int license_thread(SceSize args, void *argp);
|
||||
|
||||
#endif
|
||||
|
@ -15,6 +15,7 @@ FOLDER = "Folder"
|
||||
MOVING = "Moving..."
|
||||
COPYING = "Copying..."
|
||||
DELETING = "Deleting..."
|
||||
IMPORTING = "Importing..."
|
||||
EXPORTING = "Exporting..."
|
||||
INSTALLING = "Installing..."
|
||||
DOWNLOADING = "Downloading..."
|
||||
@ -40,6 +41,7 @@ ENTER_SEARCH_TERM = "Enter search term"
|
||||
|
||||
# Context menu strings
|
||||
REFRESH_LIVEAREA = "Refresh livearea"
|
||||
REFRESH_LICENSE_DB = "Refresh license database"
|
||||
MOUNT_UMA0 = "Mount uma0:"
|
||||
MOUNT_IMC0 = "Mount imc0:"
|
||||
MOUNT_USB_UX0 = "Mount USB ux0:"
|
||||
@ -100,6 +102,7 @@ REFRESHED = "Refreshed %d items."
|
||||
COPIED_FILE = "Copied %d file(s)."
|
||||
COPIED_FOLDER = "Copied %d folder(s)."
|
||||
COPIED_FILES_FOLDERS = "Copied %d file(s)/folder(s)."
|
||||
IMPORTED_LICENSES = "Imported %d license(s)."
|
||||
|
||||
# Dialog questions
|
||||
DELETE_FILE_QUESTION = "Are you sure you want to delete this file?"
|
||||
@ -124,6 +127,7 @@ INSTALL_BRICK_WARNING = "This package uses functions that remount
|
||||
HASH_FILE_QUESTION = "SHA1 hashing may take a long time. Continue?"
|
||||
SAVE_MODIFICATIONS = "Do you want to save your modifications?"
|
||||
REFRESH_LIVEAREA_QUESTION = "Refreshing the livearea may take a long time. Continue?"
|
||||
REFRESH_LICENSE_DB_QUESTION = "Refreshing the license database may take a long time. Continue?"
|
||||
|
||||
# HENkaku settings strings
|
||||
HENKAKU_SETTINGS = "HENkaku settings"
|
||||
|
111
rif.c
Normal file
111
rif.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
VitaShell - RIF and license.db handling
|
||||
Copyright (C) 2017 VitaSmith
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "rif.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
#define MAX_QUERY_LENGTH 128
|
||||
|
||||
int create_db(const char* db_path, const char* schema)
|
||||
{
|
||||
int rc;
|
||||
sqlite3 *db = NULL;
|
||||
|
||||
rc = sqlite3_open_v2(db_path, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto out;
|
||||
rc = sqlite3_exec(db, schema, NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
sqlite3_close(db);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Insert a new RIF into the DB
|
||||
int insert_rif(const char* db_path, const uint8_t* rif)
|
||||
{
|
||||
int rc;
|
||||
sqlite3 *db = NULL;
|
||||
sqlite3_stmt *stmt;
|
||||
char query[MAX_QUERY_LENGTH];
|
||||
|
||||
rc = sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READWRITE, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto out;
|
||||
|
||||
snprintf(query, sizeof(query), "INSERT INTO Licenses VALUES('%s', ?)", &rif[0x10]);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto out;
|
||||
rc = sqlite3_bind_blob(stmt, 1, rif, RIF_SIZE, SQLITE_STATIC);
|
||||
if (rc != SQLITE_OK)
|
||||
goto out;
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE)
|
||||
goto out;
|
||||
rc = sqlite3_finalize(stmt);
|
||||
|
||||
out:
|
||||
sqlite3_close(db);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Query a RIF from the license database. Returned data must be freed by the caller.
|
||||
uint8_t* query_rif(const char* db_path, const char* content_id)
|
||||
{
|
||||
int rc, rif_size = 0;
|
||||
sqlite3 *db = NULL;
|
||||
sqlite3_stmt *stmt;
|
||||
char query[MAX_QUERY_LENGTH];
|
||||
uint8_t *rif = NULL;
|
||||
const uint8_t *db_rif;
|
||||
|
||||
rc = sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READONLY, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto out;
|
||||
|
||||
snprintf(query, sizeof(query), "SELECT RIF FROM Licenses WHERE CONTENT_ID = '%s'", content_id);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto out;
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc == SQLITE_ROW)
|
||||
rif_size = sqlite3_column_bytes(stmt, 0);
|
||||
if (rif_size != RIF_SIZE)
|
||||
goto out;
|
||||
|
||||
rif = malloc(rif_size);
|
||||
db_rif = sqlite3_column_blob(stmt, 0);
|
||||
|
||||
if ((rif != NULL) && (db_rif != NULL))
|
||||
memcpy(rif, db_rif, rif_size);
|
||||
rc = sqlite3_finalize(stmt);
|
||||
|
||||
out:
|
||||
sqlite3_close(db);
|
||||
return rif;
|
||||
}
|
37
rif.h
Normal file
37
rif.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
VitaShell - RIF handling functions
|
||||
Copyright (C) 2017 VitaSmith
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define RIF_SIZE 512
|
||||
#define LICENSE_DB "ux0:license/license.db"
|
||||
#define LICENSE_DB_SCHEMA \
|
||||
"CREATE TABLE Licenses (" \
|
||||
"CONTENT_ID TEXT NOT NULL UNIQUE," \
|
||||
"RIF BLOB NOT NULL," \
|
||||
"PRIMARY KEY(CONTENT_ID)" \
|
||||
")"
|
||||
|
||||
int create_db(const char* db_path, const char* schema);
|
||||
int insert_rif(const char* db_path, const uint8_t* rif);
|
||||
uint8_t* query_rif(const char* db_path, const char* content_id);
|
||||
|
||||
// From sqlite3.c
|
||||
int sqlite_init();
|
||||
int sqlite_exit();
|
170
sqlite3.c
Normal file
170
sqlite3.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
PS Vita override for R/W SQLite functionality
|
||||
Copyright (C) 2017 VitaSmith
|
||||
Based on original work (C) 2015 xyzz
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
Note: We must override part of the default SQlite VFS for full SQLite
|
||||
DB access because the native Vita one, called "psp2", only allows read
|
||||
operations (which Sony probably did to avoid exploit propagation from
|
||||
potential SQLite vulnerabilities).
|
||||
Short of using the VFS overrides below, any attempt to create a new DB
|
||||
or write to an existing one will result in SQLITE_CANTOPEN...
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <psp2/io/fcntl.h>
|
||||
#include <psp2/sqlite.h>
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
//#define VERBOSE 1
|
||||
#if VERBOSE
|
||||
extern int psvDebugScreenPrintf(const char *format, ...);
|
||||
#define LOG psvDebugScreenPrintf
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
#define IS_ERROR(x) ((unsigned)x & 0x80000000)
|
||||
|
||||
static sqlite3_io_methods* rw_methods = NULL;
|
||||
static sqlite3_vfs *rw_vfs = NULL;
|
||||
|
||||
// The file structure used by Sony
|
||||
typedef struct {
|
||||
sqlite3_file file;
|
||||
int* fd;
|
||||
} vfs_file;
|
||||
|
||||
static int vita_xWrite(sqlite3_file *file, const void *buf, int count, sqlite_int64 offset)
|
||||
{
|
||||
vfs_file *p = (vfs_file*)file;
|
||||
int seek = sceIoLseek(*p->fd, offset, SCE_SEEK_SET);
|
||||
LOG("seek %x %x => %x\n", *p->fd, offset, seek);
|
||||
if (seek != offset)
|
||||
return SQLITE_IOERR_WRITE;
|
||||
int write = sceIoWrite(*p->fd, buf, count);
|
||||
LOG("write %x %x %x => %x\n", *p->fd, buf, count);
|
||||
if (write != count) {
|
||||
LOG("write error %08x\n", write);
|
||||
return SQLITE_IOERR_WRITE;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int vita_xOpen(sqlite3_vfs *vfs, const char *name, sqlite3_file *file, int flags, int *out_flags)
|
||||
{
|
||||
sqlite3_vfs* org_vfs = (sqlite3_vfs*)vfs->pAppData;
|
||||
|
||||
LOG("open %s: flags = %08x, ", name, flags);
|
||||
// Default xOpen() does not create files => do that ourselves
|
||||
// TODO: handle SQLITE_OPEN_EXCLUSIVE
|
||||
if (flags & SQLITE_OPEN_CREATE) {
|
||||
SceUID fd = sceIoOpen(name, SCE_O_RDONLY, 0777);
|
||||
if (IS_ERROR(fd))
|
||||
fd = sceIoOpen(name, SCE_O_WRONLY | SCE_O_CREAT, 0777);
|
||||
if (!IS_ERROR(fd))
|
||||
sceIoClose(fd);
|
||||
}
|
||||
|
||||
// Call the original xOpen()
|
||||
int r = org_vfs->xOpen(org_vfs, name, file, flags, out_flags);
|
||||
vfs_file *p = (vfs_file*)file;
|
||||
LOG("fd = %08x, r = %d\n", (p == NULL) ? 0 : *p->fd, r);
|
||||
|
||||
// Default xOpen() also forces read-only on SQLITE_OPEN_READWRITE files
|
||||
if ((file->pMethods != NULL) && (flags & SQLITE_OPEN_READWRITE)) {
|
||||
if (!IS_ERROR(*p->fd)) {
|
||||
// Reopen the file with write access
|
||||
sceIoClose(*p->fd);
|
||||
*p->fd = sceIoOpen(name, SCE_O_RDWR, 0777);
|
||||
LOG("override fd = %08x\n", *p->fd);
|
||||
if (IS_ERROR(*p->fd))
|
||||
return SQLITE_IOERR_WRITE;
|
||||
}
|
||||
// Need to override xWrite() as well
|
||||
if (rw_methods == NULL) {
|
||||
rw_methods = malloc(sizeof(sqlite3_io_methods));
|
||||
if (rw_methods != NULL) {
|
||||
memcpy(rw_methods, file->pMethods, sizeof(sqlite3_io_methods));
|
||||
rw_methods->xWrite = vita_xWrite;
|
||||
}
|
||||
}
|
||||
if (rw_methods != NULL)
|
||||
file->pMethods = rw_methods;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int vita_xDelete(sqlite3_vfs *vfs, const char *name, int syncDir)
|
||||
{
|
||||
int ret = sceIoRemove(name);
|
||||
LOG("delete %s: 0x%08x\n", name, ret);
|
||||
if (IS_ERROR(ret))
|
||||
return SQLITE_IOERR_DELETE;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int sqlite_init()
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (rw_vfs != NULL)
|
||||
return SQLITE_OK;
|
||||
|
||||
SceSqliteMallocMethods mf = {
|
||||
(void* (*) (int)) malloc,
|
||||
(void* (*) (void*, int)) realloc,
|
||||
free
|
||||
};
|
||||
sceSqliteConfigMallocMethods(&mf);
|
||||
|
||||
rw_vfs = malloc(sizeof(sqlite3_vfs));
|
||||
sqlite3_vfs *vfs = sqlite3_vfs_find(NULL);
|
||||
if ((vfs != NULL) && (rw_vfs != NULL)) {
|
||||
// Override xOpen() and xDelete()
|
||||
memcpy(rw_vfs, vfs, sizeof(sqlite3_vfs));
|
||||
rw_vfs->zName = "psp2_rw";
|
||||
rw_vfs->xOpen = vita_xOpen;
|
||||
rw_vfs->xDelete = vita_xDelete;
|
||||
// Keep a copy of the original vfs pointer
|
||||
rw_vfs->pAppData = vfs;
|
||||
rc = sqlite3_vfs_register(rw_vfs, 1);
|
||||
if (rc != SQLITE_OK) {
|
||||
LOG("sqlite_init: could not register vfs: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int sqlite_exit()
|
||||
{
|
||||
int rc = SQLITE_OK;
|
||||
free(rw_methods);
|
||||
rw_methods = NULL;
|
||||
if (rw_vfs != NULL) {
|
||||
rc = sqlite3_vfs_unregister(rw_vfs);
|
||||
if (rc != SQLITE_OK)
|
||||
LOG("sqlite_exit: error unregistering vfs: %d\n", rc);
|
||||
free(rw_vfs);
|
||||
rw_vfs = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
127
sqlite3.h
127
sqlite3.h
@ -10,8 +10,8 @@
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This header was intentionally truncated from the original version to
|
||||
** only include the elements needed by this application.
|
||||
** This header was intentionally truncated from the original version
|
||||
** to include only the elements needed by our application.
|
||||
**
|
||||
*/
|
||||
|
||||
@ -60,6 +60,35 @@ extern "C" {
|
||||
#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
|
||||
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
|
||||
|
||||
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
|
||||
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
|
||||
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
|
||||
#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
|
||||
#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
|
||||
#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8))
|
||||
#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8))
|
||||
#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8))
|
||||
#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8))
|
||||
#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8))
|
||||
#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8))
|
||||
#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8))
|
||||
#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8))
|
||||
#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8))
|
||||
#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8))
|
||||
#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8))
|
||||
#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8))
|
||||
#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8))
|
||||
#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8))
|
||||
#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8))
|
||||
#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8))
|
||||
#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
|
||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
||||
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
|
||||
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
||||
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
||||
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
||||
|
||||
#define SQLITE_OPEN_READONLY 0x00000001
|
||||
#define SQLITE_OPEN_READWRITE 0x00000002
|
||||
#define SQLITE_OPEN_CREATE 0x00000004
|
||||
@ -69,25 +98,101 @@ extern "C" {
|
||||
#define SQLITE_OPEN_SHAREDCACHE 0x00020000
|
||||
#define SQLITE_OPEN_PRIVATECACHE 0x00040000
|
||||
|
||||
#ifdef SQLITE_INT64_TYPE
|
||||
typedef SQLITE_INT64_TYPE sqlite_int64;
|
||||
typedef unsigned SQLITE_INT64_TYPE sqlite_uint64;
|
||||
#elif defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
typedef __int64 sqlite_int64;
|
||||
typedef unsigned __int64 sqlite_uint64;
|
||||
#else
|
||||
typedef long long int sqlite_int64;
|
||||
typedef unsigned long long int sqlite_uint64;
|
||||
#endif
|
||||
typedef sqlite_int64 sqlite3_int64;
|
||||
typedef sqlite_uint64 sqlite3_uint64;
|
||||
|
||||
typedef void (*sqlite3_destructor_type)(void*);
|
||||
#define SQLITE_STATIC ((sqlite3_destructor_type)0)
|
||||
#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
|
||||
|
||||
typedef struct sqlite3 sqlite3;
|
||||
typedef struct sqlite3_stmt sqlite3_stmt;
|
||||
|
||||
SQLITE_API int sqlite3_errcode(sqlite3 *db);
|
||||
SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
|
||||
typedef struct sqlite3_file sqlite3_file;
|
||||
struct sqlite3_file {
|
||||
const struct sqlite3_io_methods *pMethods;
|
||||
};
|
||||
|
||||
typedef struct sqlite3_io_methods sqlite3_io_methods;
|
||||
struct sqlite3_io_methods {
|
||||
int iVersion;
|
||||
int(*xClose)(sqlite3_file*);
|
||||
int(*xRead)(sqlite3_file*, void*, int, sqlite3_int64);
|
||||
int(*xWrite)(sqlite3_file*, const void*, int, sqlite3_int64);
|
||||
int(*xTruncate)(sqlite3_file*, sqlite3_int64);
|
||||
int(*xSync)(sqlite3_file*, int);
|
||||
int(*xFileSize)(sqlite3_file*, sqlite3_int64*);
|
||||
int(*xLock)(sqlite3_file*, int);
|
||||
int(*xUnlock)(sqlite3_file*, int);
|
||||
int(*xCheckReservedLock)(sqlite3_file*, int*);
|
||||
int(*xFileControl)(sqlite3_file*, int, void*);
|
||||
int(*xSectorSize)(sqlite3_file*);
|
||||
int(*xDeviceCharacteristics)(sqlite3_file*);
|
||||
int(*xShmMap)(sqlite3_file*, int, int, int, void volatile**);
|
||||
int(*xShmLock)(sqlite3_file*, int, int, int);
|
||||
void(*xShmBarrier)(sqlite3_file*);
|
||||
int(*xShmUnmap)(sqlite3_file*, int);
|
||||
};
|
||||
|
||||
typedef struct sqlite3_vfs sqlite3_vfs;
|
||||
typedef void(*sqlite3_syscall_ptr)(void);
|
||||
struct sqlite3_vfs {
|
||||
int iVersion;
|
||||
int szOsFile;
|
||||
int mxPathname;
|
||||
sqlite3_vfs *pNext;
|
||||
const char *zName;
|
||||
void *pAppData;
|
||||
int(*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, int flags, int *pOutFlags);
|
||||
int(*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
|
||||
int(*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
|
||||
int(*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut);
|
||||
void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename);
|
||||
void(*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);
|
||||
void(*(*xDlSym)(sqlite3_vfs*, void*, const char *zSymbol))(void);
|
||||
void(*xDlClose)(sqlite3_vfs*, void*);
|
||||
int(*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);
|
||||
int(*xSleep)(sqlite3_vfs*, int microseconds);
|
||||
int(*xCurrentTime)(sqlite3_vfs*, double*);
|
||||
int(*xGetLastError)(sqlite3_vfs*, int, char *);
|
||||
int(*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*);
|
||||
int(*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr);
|
||||
sqlite3_syscall_ptr(*xGetSystemCall)(sqlite3_vfs*, const char *zName);
|
||||
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
|
||||
};
|
||||
|
||||
SQLITE_API int sqlite3_errcode(sqlite3*);
|
||||
SQLITE_API int sqlite3_extended_errcode(sqlite3*);
|
||||
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
|
||||
SQLITE_API void *sqlite3_malloc(int);
|
||||
SQLITE_API void *sqlite3_realloc(void*, int);
|
||||
SQLITE_API void sqlite3_free(void*);
|
||||
SQLITE_API int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
|
||||
SQLITE_API int sqlite3_close(sqlite3 *);
|
||||
SQLITE_API int sqlite3_exec(sqlite3*, const char *sql, int (*callback)(void*,int,char**,char**), void *, char **errmsg);
|
||||
SQLITE_API int sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail);
|
||||
SQLITE_API int sqlite3_open(const char*, sqlite3**);
|
||||
SQLITE_API int sqlite3_open_v2(const char *filename, sqlite3**, int, const char*);
|
||||
SQLITE_API int sqlite3_close(sqlite3*);
|
||||
SQLITE_API int sqlite3_exec(sqlite3*, const char *sql, int (*)(void*,int,char**,char**), void*, char**);
|
||||
SQLITE_API int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);
|
||||
SQLITE_API int sqlite3_step(sqlite3_stmt*);
|
||||
SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
|
||||
SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
|
||||
SQLITE_API int sqlite3_finalize(sqlite3_stmt*);
|
||||
SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int);
|
||||
SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int);
|
||||
SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int, void(*)(void*));
|
||||
SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *);
|
||||
SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int);
|
||||
SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user